Actually we have a problem here. As we don’t specify the Content-Length in streamed entity, Play will have to compute it itself, and the only way to do this is to consume the whole source content and load it into memory, and then compute the response size.

That’s a problem for large files that we don’t want to load completely into memory. So to avoid that, we just have to specify the Content-Length header ourselves.

This helper will also compute the Content-Type header from the file name, and add the Content-Disposition header to specify how the web browser should handle this response. The default is to show this file inline by adding the header Content-Disposition: inline; filename=fileToServe.pdf to the HTTP response.

Now you don’t have to specify a file name since the web browser will not try to download it, but will just display the file content in the web browser window. This is useful for content types supported natively by the web browser, such as text, HTML or images.

For now, this works well with streaming file content, since we are able to compute the content length before streaming it. But what about dynamically-computed content with no content size available?

For this kind of response we have to use Chunked transfer encoding.

Chunked transfer encoding is a data transfer mechanism in version HTTP 1.1 in which a web server serves content in a series of chunks. This uses the Transfer-Encoding HTTP response header instead of the Content-Length header, which the protocol would otherwise require. Because the Content-Length header is not used, the server does not need to know the length of the content before it starts transmitting a response to the client (usually a web browser). Web servers can begin transmitting responses with dynamically-generated content before knowing the total size of that content.

The size of each chunk is sent right before the chunk itself so that a client can tell when it has finished receiving data for that chunk. The data transfer is terminated by a final chunk of length zero.

The advantage is that we can serve data live, meaning that we send chunks of data as soon as they are available. The drawback is that since the web browser doesn’t know the content size, it is not able to display a proper download progress bar.

Let’s say that we have a service somewhere that provides a dynamic InputStream that computes some data. We can ask Play to stream this content directly using a chunked response:

The method Source.actorRef creates an Akka Streams Source that materializes to an ActorRef. You can then publish elements to the stream by sending messages to the actor. An alternative approach is to create an actor that extends ActorPublisher and use the Stream.actorPublisher method to create it.