The error java.io.IOException: Stream Closed
indicates that the InputStream
passed to the InputStreamResource
has already been closed before it is read by the Spring framework to create the response.
Use Case
The use case where you may need to encounter this error is file download from Spring Boot App. Writing down bytes loaded in memory – as a Base64 String may be an option – only if you know that the files will not be very big. For anything above one megabyte the proper way to handle it is with Stream – lazy – without loading it all to memory.
Here are common reasons this might happen:
1. The InputStream is closed before being used
- If you have explicitly closed the
InputStream
somewhere in your code after wrapping it in theInputStreamResource
, this will result in theStream Closed
exception when the framework tries to read it.
Example issue:
InputStream inputStream = new FileInputStream(file);
inputStream.close(); // Closing the stream prematurely
return ResponseEntity.ok()
.body(new InputStreamResource(inputStream)); // Fails because the stream is closed
2. InputStream is auto-closed
- If the
InputStream
was opened in a try-with-resources block, it will be automatically closed at the end of the block before theResponseEntity
can use it.
Example issue:
try (InputStream inputStream = new FileInputStream(file)) {
return ResponseEntity.ok()
.body(new InputStreamResource(inputStream)); // Stream is closed when try block ends
}
3. Reusing a closed InputStream
- If the
InputStream
is read somewhere else before being passed to theInputStreamResource
, the stream pointer reaches the end of the stream, and the stream might also be closed by the code consuming it.
Example issue:
InputStream inputStream = new FileInputStream(file);
byte[] buffer = inputStream.readAllBytes(); // Consumes the stream
// Passing the same stream to InputStreamResource will fail
return ResponseEntity.ok()
.body(new InputStreamResource(inputStream)); // Stream already consumed or closed
Solutions
- Ensure the InputStream is open and valid
- Make sure the
InputStream
you pass toInputStreamResource
is not closed or consumed beforehand.
Correct Code:
InputStream inputStream = new FileInputStream(file); return ResponseEntity.ok() .contentType(MediaType.APPLICATION_OCTET_STREAM) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"") .body(new InputStreamResource(inputStream));
- Make sure the
- Avoid try-with-resources for InputStream
- Do not use try-with-resources if you plan to pass the
InputStream
toInputStreamResource
, as it closes the stream automatically.
- Do not use try-with-resources if you plan to pass the
- Use a fresh InputStream for each request
- If you are reading from a file, create a new
FileInputStream
for every request.
Example:
return ResponseEntity.ok() .contentType(MediaType.APPLICATION_OCTET_STREAM) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"") .body(new InputStreamResource(new FileInputStream(file)));
- If you are reading from a file, create a new
- Buffered InputStream for reusable streams
- If the stream is large and you want better performance, consider wrapping it in a
BufferedInputStream
to prevent accidental closure or reading.
- If the stream is large and you want better performance, consider wrapping it in a
- Debugging Tip
- Add logging or debugging to check if the
InputStream
is being consumed or closed elsewhere in your code.
- Add logging or debugging to check if the
Let me know if you’d like help analyzing your specific code structure!
Will Spring close the stream?
Yes, Spring will close the InputStream
when it is used in a ResponseEntity
with InputStreamResource
. This is because Spring considers it the owner of the stream and ensures proper resource cleanup after the response is written.
Check out my Spring Boot Web Apps: https://programtom.com/dev/product-category/technologies/spring-boot-framework/?orderby=date-desc