Reputation: 2342
Consider I have a large amount of csv strings in database and I need to pass them to the client by his request.
A naive way to do this:
@RequestMapping(value = "/{user}/ejournal", method = RequestMethod.GET, produces = "text/csv")
public ResponseEntity<ByteArrayResource> getEJournal(@PathVariable Long userId) {
final byte[] documentBody = EJournal
.findByUser(userId)
.stream()
.collect(Collectors.joining("\n"))
.getBytes();
return new ResponseEntity(
new ByteArrayResource(documentBody),
HttpStatus.OK);
}
From the client side its straightforward (kotlin code example):
val inputStream = eJournalService.getJournal(userId)
inputStream.reader().forEachLine { line -> ... }
But I am forced to load the entire data to memory before pass it to the stream and obviously it is not efficient.
So I need somehow to bufferize it to read data with pagination using custom reader and send buffers to client. I thought to implement my own InputStream
but InputStream#read()
returns int
instead of String
and it is a bit complicated.
Any best practices to do this? I tried to search but there are only examples to pass a picture using stream which is already in memory, not with buffered pagination with sequential database queries after batch sends.
Thanks in advance.
Upvotes: 1
Views: 678
Reputation: 691635
From the documentation
The following are the supported return types:
[...]
- void if the method handles the response itself (by writing the response content directly, declaring an argument of type ServletResponse / HttpServletResponse for that purpose)
[...]
- A StreamingResponseBody can be returned to write to the response OutputStream asynchronously; also supported as the body within a ResponseEntity.
So you can just do
public StreamingResponseBody getEJournal(@PathVariable Long userId) {
return outputStream -> {
// write your lines to the outputStream here
};
}
You can also wrote to the stream synchronously by just having the HttpServletResponse
as argument of your method, and write to its output stream.
Upvotes: 1