fasth
fasth

Reputation: 2342

Gradually populate InputStream and return it with SpringMVC

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

Answers (1)

JB Nizet
JB Nizet

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

Related Questions