2

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.

user207421
  • 305,947
  • 44
  • 307
  • 483
Nikolas
  • 2,322
  • 9
  • 33
  • 55
  • 1
    I don't know `InputStreamResource` but I'd assume that if that resource is closed the stream will be closed as well. – Thomas Sep 24 '19 at 11:59
  • This is because you close the stream before giving it to spring handler so it cannot read it's content. It's always the stream's consumer's responsibility to close the stream which in this case is the spring handler, and it does so. – Daniel Hári Apr 25 '23 at 14:21

1 Answers1

6

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;
Community
  • 1
  • 1
LHCHIN
  • 3,679
  • 2
  • 16
  • 34
  • 4
    I suppose that `ResourceHttpMessageConverter` closes the stream: https://stackoverflow.com/questions/48660011/how-to-handle-io-streams-in-spring-mvc/48660203#48660203 – mkczyk Nov 20 '19 at 13:21