Search code examples
javastreamspring-restcontrollertry-with-resources

InputStream is closed in spring controller


Using spring controller, endpoint returns file in the body response. I want to be sure to not to get resource leaks using "try with resources", but in that way in postman I get an error:

"error": "Internal Server Error", "message": "Stream Closed",

snippet of code in Spring controller:

InputStreamResource result;
ResponseEntity<Resource> response;
try(FileInputStream ios = new FileInputStream(file)){
    result = new InputStreamResource(ios);
    response = ResponseEntity.ok()
        .headers(/*some headers here*/)
        .contentLength(file.length())
        .contentType(/*some media type here*/)
        .body(result);
    logger.info("successfully created");
    return response;
} catch (IOException e) {
        //some code here..
}

Interesting, that in logs I got success message, but in postman or in browser (this is a GET request) I got an error.

And it would work, if not to use "try-with-resource", but I'm afraid of resource leaks in that way.


Solution

  • Because try with resource will call close() before return, a "Stream Closed" error will occur.
    A simple way is to directly put the instance of InputStreamResource in .body(), and most examples on web do this as well. However, I am not sure whether the resource will be closed correctly to prevent application from resource leak.

    response = ResponseEntity.ok()
        .contentLength(file.length())
        .body(new InputStreamResource(new FileInputStream(file)));
    return response;
    

    Another way, if you want to stream the response, you can use StreamingResponseBody.

    Interface StreamingResponseBody (quoted from Spring website)

    This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.

    Sample code:

    StreamingResponseBody responseBody = outputStream -> {
        Files.copy(file.toPath(), outputStream);
    };
    
    response.ok()
        .contentLength(file.length())
        .body(responseBody);
    return response;