Reputation: 83245
Ithink this used to be possible in Play 1.x, but I can't find how to do it in Play 2.x
I know that Play is asynchronous and uses Iteratee
s. However, there is generally much better support for InputStream
s.
(In this case, I will be using a streaming JSON parser like Jackson to process the request body.)
How can I get an InputStream
from a chunked request body?
Upvotes: 3
Views: 1295
Reputation: 32284
I was able to get this working with the following code:
// I think all of the parens and braces line up -- copied/pasted from code
val pos = new PipedOutputStream()
val pis = new PipedInputStream(pos)
val result = Promise[Either[Errors, String]]()
def outputStreamBodyParser = {
BodyParser("outputStream") {
requestHeader =>
val length = requestHeader.headers(HeaderNames.CONTENT_LENGTH).toLong
Future {
result.completeWith(saveFile(pis, length)) // save returns Future[Either[Errors, String]]
}
Iteratee.fold[Array[Byte], OutputStream](pos) {
(os, data) =>
os.write(data)
os
}.map {
os =>
os.close()
Right(os)
}
}
}
Action.async(parse.when(
requestHeaders => {
val maybeContentLength = requestHeaders.headers.get(HeaderNames.CONTENT_LENGTH)
maybeContentLength.isDefined && allCatch.opt(maybeContentLength.get.toLong).isDefined
},
outputStreamBodyParser,
requestHeaders => Future.successful(BadRequest("Missing content-length header")))) {
request =>
result.future.map {
case Right(fileRef) => Ok(fileRef)
case Left(errors) => BadRequest(errors)
}
}
Upvotes: 2
Reputation: 13667
Play 2 is meant to be fully asynchronous, so this isn't easily possible or desirable. The problem with InputStream
is there is no push back, there is no way for the reader of the InputStream
to communicate to the input that it wants more data without blocking on read
. Technically it is possible to write an Iteratee
that could read data and put it into an InputStream
, and would wait for a call to read
on the InputStream
before asking the Enumerator
for more data, but it would be dangerous. You would have to make sure that the InputStream
was closed properly, or the Enumerator
would sit waiting forever (or until it times out) and the call to read
must be made from a thread that is not running on the same ExecutionContext
as the Enumerator
and Iteratee
or the application could deadlock.
Upvotes: 1