Reputation: 24555
I have a simple Spring Boot
controller that serves zip-files, which can get fairly large. So in order to not having to load the entire file content into memory I'm creating an InputStream
from the file to be read and return a StreamingResponseBody
like this:
import org.springframework.util.StreamUtils;
@RestController
@RequestMapping("/api/export")
public class ExportController {
@GetMapping(value = "/{fileUuid}")
public StreamingResponseBody exportFile(@PathVariable String fileUuid, HttpServletResponse response) {
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=\"export.zip\"");
InputStream inputStream = new FileInputStream(this.resolveFile(fileUuid));
return outputStream -> {
StreamUtils.copy(inputStream, outputStream); // also tried inputStream.transferTo(outputStream);
};
}
public File resolveFile(String fileUuid) {
// ...
}
}
Now, this works fine for the very first request - but after that around 80% of requests are returned with an empty response body (the http-response just contains response headers). We're using JDK11
and Spring Boot 2.4.3
.
What am I doing wrong here?
EDIT:
Strangely enough, I was able to solve the issue by wrapping the FileInputStream
in a InputStreamResource
and returning this resource instead of a StreamingResponseBody
:
// ...
var resource = new InputStreamResource(new FileInputStream(this.resolveFile(fileUuid)));
return resource;
But I'm still curious of what's wrong with the original approach..
Upvotes: 2
Views: 2229
Reputation: 66
Had a similar problem and it turns out you might have a synchronous stream copy in filter happening before the asynchronous call filling the response outputStream. Check if disabling other servlet filters makes it work. also additional logging will help in revealing the issue (check response data size, see whether it grows at some point in the filter chain).
Upvotes: 5