You can use Concurrent.joined
:
val (iteratee, enumerator) = Concurrent.joined[Array[Byte]]
Question
I wanted to write an app in Play! framework 2.2.x as an exercise in asynchronous approach and the play.api.libs.iterattee
package. What I wanted to do was get a POST request with a large file upload in its body and send it, chunk by chunk, as a download to a separate request.
The "Play for Scala" book and this example lead me receiving the file within the request in chunks I can accumulate or iterate over in any Iteratee
I define in a custom BodyParser
. When it comes to serving a chunked response, I need a provide an Enumerator
to the response - like Ok.chunked(enumerator)
and it will be served to the client as the Enumerator provides new chunks.
Now that I want to put it all together, I can't seem to find an elegant and reactive way to propagate asynchronously the new data chunks from the uploading client to the downloading. There doesn't seem to be a good way to "forward" data received by an Iteratee
(in this case in the BodyParser
) to a new Enumerator
(in this case the one providing output to the response).
I have come up with two ways of doing that, but neither of them seems good enough for me:
use play.api.libs.iteratee.Concurrent.boradcast
to generate a Channel
and an Enumerator
and within my BodyParser push each received chunk to the Channel
. The Channels
don't have the property that the Enumerator
s have - the Enumerator doesn't produce a new value until the Iteratee
applied over it consumes the old one.
If I were to do it this way, if the uploader had a faster internet connection than the downloader, I would have a "swelling" Channel
using up more and more memory, instead of handling one chunk at a time.
create a proxy akka Actor
and send the uploaded file, chunk by chunk to it, only sending the next one after the last one has been consumed by the downloader. But in this case the confirmation of each chunk being sent to the downloader would have to go through the Actor
proxy so that the uploader Iteratee
would send another chunk to the proxy. This seems like unnecessary overhead for me.
I guess my question is twofold:
Enumerator
generating the values received by an Iteratee
?La solution
You can use Concurrent.joined
:
val (iteratee, enumerator) = Concurrent.joined[Array[Byte]]