source

ResponseEntity를 사용하여 InputStream을 닫는 적절한 스트리밍 방법

lovecheck 2023. 2. 26. 09:51
반응형

ResponseEntity를 사용하여 InputStream을 닫는 적절한 스트리밍 방법

어플리케이션 중 하나가 파일핸들이 누수되어 아직 원인을 찾지 못했습니다.

코드에는 다음과 같은 기능이 몇 가지 있습니다.

public ResponseEntity<InputStreamResource> getFoo( ... ) {
    InputStream content = getContent(...)
    InputStreamResource isr = new InputStreamResource(content);
    return ResponseEntity.status(HttpServletResponse.SC_OK).body(isr);
}

(if체크 및try/catch간략화를 위해 삭제)

이 섹션이 문제의 원인인 것은 확실합니다.이 특정 코드를 JMeter로 로드하면 다음과 같은 것을 알 수 있기 때문입니다.getContent()이 단계에서는 실패합니다.

is = Files.newInputStream(f.toPath());

보통 나는 문을 닫는다.InputStream이 짧고 간단한 코드 때문에 전에는 스트림을 닫을 수 없습니다.return또는 의 호출body.

내가 달릴 때lsof(Linux에서 코드가 실행됨) 수천 개의 파일이 읽기 모드로 열려 있는 것을 알 수 있습니다.그래서 나는 이 문제가 스트림이 닫히지 않아서 생긴 것이라고 확신한다.

거래해야 할 모범 사례 코드가 있습니까?

StreamingResponseBody를 사용해 볼 수 있습니다.

StreamingResponseBody

응용 프로그램이 Servlet 컨테이너 스레드를 유지하지 않고 응답 OutputStream에 직접 쓸 수 있는 비동기 요청 처리를 위한 컨트롤러 메서드 반환 값 유형입니다.

다른 스레드에서 작업하고 있기 때문에 응답에 직접 쓰고 있는 문제, 호출해야 할 문제close()전에return해결되었습니다.

아마 당신은 다음 예시로 시작할 수 있을 것이다.

public ResponseEntity<StreamingResponseBody> export(...) throws FileNotFoundException {
    //...

    InputStream inputStream = new FileInputStream(new File("/path/to/example/file"));


    StreamingResponseBody responseBody = outputStream -> {

        int numberOfBytesToWrite;
        byte[] data = new byte[1024];
        while ((numberOfBytesToWrite = inputStream.read(data, 0, data.length)) != -1) {
            System.out.println("Writing some bytes..");
            outputStream.write(data, 0, numberOfBytesToWrite);
        }

        inputStream.close();
    };

    return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=generic_file_name.bin")
            .contentType(MediaType.APPLICATION_OCTET_STREAM)
            .body(responseBody);
}

또, 다음과 같은 기능을 사용해 볼 수도 있습니다.Files(Java 7 이후)

관리하지 않아도 됩니다.InputStream

    File file = new File("/path/to/example/file");

    StreamingResponseBody responseBody = outputStream -> {
        Files.copy(file.toPath(), outputStream);
    };

@Stackee007이 코멘트로 기술한 바와 같이, 프로덕션 환경에서 부하가 높은 경우, 다음을 정의하는 것이 좋습니다.@Configuration을 위한 클래스TaskExecutor매개 변수를 조정하고 관리합니다.Async과정.

@Configuration
@EnableAsync
@EnableScheduling
public class AsyncConfiguration implements AsyncConfigurer {

    private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class);

    private final TaskExecutionProperties taskExecutionProperties;

    public AsyncConfiguration(TaskExecutionProperties taskExecutionProperties) {
        this.taskExecutionProperties = taskExecutionProperties;
    }

    //  ---------------> Tune parameters here
    @Override
    @Bean(name = "taskExecutor")
    public Executor getAsyncExecutor() {
        log.debug("Creating Async Task Executor");
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(taskExecutionProperties.getPool().getCoreSize());
        executor.setMaxPoolSize(taskExecutionProperties.getPool().getMaxSize());
        executor.setQueueCapacity(taskExecutionProperties.getPool().getQueueCapacity());
        executor.setThreadNamePrefix(taskExecutionProperties.getThreadNamePrefix());
        return executor;
    }
    
    //  ---------------> Use this task executor also for async rest methods
    @Bean
    protected WebMvcConfigurer webMvcConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
                configurer.setTaskExecutor(getTaskExecutor());
            }
        };
    }

    @Bean
    protected ConcurrentTaskExecutor getTaskExecutor() {
        return new ConcurrentTaskExecutor(this.getAsyncExecutor());
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new SimpleAsyncUncaughtExceptionHandler();
    }
}

mockMvc로 테스트하는 방법

통합 테스트에서 이 샘플 코드를 따를 수 있습니다.

    .andExpect(request().asyncStarted())
    .andDo(MvcResult::getAsyncResult)
    .andExpect(status().isOk()).getResponse().getContentAsByteArray();

콘텐츠 타입ResponseEntity<StreamingResponseBody>는 입니다.MediaType.APPLICATION_OCTET_STREAM이 예에서는 바이트[] (.getContentAsByteArray()) 단, 본문 응답 콘텐츠 유형에 따라 String/Json/Plaintext를 모두 얻을 수 있습니다.

스프링을 사용하는 경우 메서드는 리소스를 반환하고 스프링이 나머지(기본 스트림 닫기 포함)를 처리하도록 할 수 있습니다.Spring API 내에서 이용할 수 있는 리소스 구현은 거의 없습니다.그렇지 않으면 독자적인 구현이 필요합니다.결국, 당신의 방법은 간단해지고 다음과 같은 것을 원합니다.

public ResponseEntity<Resource> getFo0(...) {
    return new InputStreamResource(<Your input stream>);
}

로컬 파일을 읽고 내용을 HTTP 응답 본문으로 설정하는 모든 컨트롤러 메서드를 리팩터링할 수 있습니다.

「 」를하는 대신에, 「 」를 사용합니다.ResponseEntity을 합니다.HttpServletResponse를 복사해 주세요.getContent(...) HttpServletResponse Commons( Commons)의 IO 관련 IO 는 、 Google Guava 、 io io io 。어떤 경우에도 입력 스트림을 닫아야 합니다!다음 코드는 문 끝에 선언된 입력 스트림을 닫는 '리소스와 함께 시도' 문을 사용하여 이를 암묵적으로 수행합니다.

@RequestMapping(value="/foo", method=RequestMethod.GET)
public void getFoo(HttpServletResponse response) {
    // use Java7+ try-with-resources
    try (InputStream content = getContent(...)) {

        // if needed set content type and attachment header
        response.addHeader("Content-disposition", "attachment;filename=foo.txt");
        response.setContentType("txt/plain");

        // copy content stream to the HttpServletResponse's output stream
        IOUtils.copy(myStream, response.getOutputStream());

        response.flushBuffer();
    }
}

참조:

https://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html https://docs.oracle.com/javase/7/docs/api/java/lang/AutoCloseable.html https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html https://google.github.io/guava/releases/19.0/api/docs/com/google/common/io/ByteStreams.html https://commons.apache.org/proper/commons-io/javadocs/api-release/index.html

방법(예: 방법, 방법, 방법)에 살펴보시기 바랍니다.public static int copy(InputStream input, OutputStream output) throws IOException ★★★★★★★★★★★★★★★★★」public static int copyLarge(InputStream input, OutputStream output) throws IOExceptionorg.apache.commons.io.IOUtils)

이것은InputStream파일에서 코드는.이치노력하다

FileSystemResource fsr = new FileSystemResource(fileName);
return ResponseEntity.status(HttpServletResponse.SC_OK).body(fsr);

FileSystemResource 할 수 있다java.util.File ,a ,ajava.nio.file.Path 또는 a a a or도String관련 파일을 가리킵니다.

언급URL : https://stackoverflow.com/questions/51845228/proper-way-of-streaming-using-responseentity-and-making-sure-the-inputstream-get

반응형