Как получить InputStream из запроса в Play

Я думаю, что это было возможно в Play 1.x, но я не могу найти, как это сделать в Play 2.x.

Я знаю, что Play асинхронный и использует Iteratees. Однако, как правило, поддержка InputStreams намного лучше.

(В этом случае я буду использовать потоковый синтаксический анализатор JSON, такой как Jackson, для обработки тела запроса.)

Как я могу получить InputStream из тела запроса, разбитого на части?


person Paul Draper    schedule 26.07.2014    source источник


Ответы (2)


Я смог заставить это работать со следующим кодом:

// 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)
  }
}
person Ralph    schedule 02.10.2014

Воспроизведение 2 должно быть полностью асинхронным, поэтому это непросто возможно или желательно. Проблема с InputStream заключается в том, что нет обратной связи, читатель InputStream не может сообщить вводу, что он хочет больше данных, не блокируя read. Технически можно написать Iteratee, который мог бы считывать данные и помещать их в InputStream, и ждать вызова read на InputStream, прежде чем запрашивать у Enumerator дополнительные данные, но это было бы опасно. Вы должны были бы убедиться, что InputStream был правильно закрыт, иначе Enumerator будет сидеть в ожидании вечно (или пока не истечет время ожидания), и вызов read должен быть сделан из потока, который не работает на том же ExecutionContext, что и Enumerator и Iteratee иначе приложение может заблокироваться.

person wingedsubmariner    schedule 27.07.2014