问题描述
我正在使用 Scala 使用 Play Framework 2.3.x 构建微服务(我是这两个方面的初学者),但我无法找到一种方法来流式传输我的请求正文.
I'm building a micro-service using Play Framework 2.3.x using Scala (I'm a beginner in both) but I can't figure out a way to stream my request body.
问题来了:
我需要一个端点 /transform
在那里我可以接收一个巨大的 TSV 文件,我将解析并以另一种格式呈现:简单转换.问题是我的控制器中的每个命令都太晚了".它在启动代码之前等待接收完整文件.
I need an endpoint /transform
where I can receive a huge TSV file that I will parse and render in another format: simple transformation. The problem is that every single command in my controller is ran "too late". It waits to receive the full file before starting the code.
示例:
def transform = Action.async {
Future {
Logger.info("Too late")
Ok("A response")
}
}
我希望能够在上传过程中逐行读取请求正文并处理请求,而不必等待文件被完全接收.
I want to be able to read line-by-line the request body during its upload and process already the request without having to wait for the file to be received completely.
欢迎提供任何提示.
推荐答案
此答案适用于 Play 2.5.x 及更高版本,因为它使用 Akka 流 API,该 API 取代了该版本中 Play 基于 Iteratee 的流.
This answer applies to Play 2.5.x and higher since it uses the Akka streams API that replaced Play's Iteratee-based streaming in that version.
基本上,您可以创建一个返回 Source[T]
的正文解析器,您可以将其传递给 Ok.chunked(...)
.一种方法是在正文解析器中使用 Accumulator.source[T]
.例如,一个刚刚逐字返回发送给它的数据的操作可能如下所示:
Basically, you can create a body parser that returns a Source[T]
that you can pass to Ok.chunked(...)
. One way to do this is to use Accumulator.source[T]
in the body parser. For example, an action that just returned data sent to it verbatim might look like this:
def verbatimBodyParser: BodyParser[Source[ByteString, _]] = BodyParser { _ =>
// Return the source directly. We need to return
// an Accumulator[Either[Result, T]], so if we were
// handling any errors we could map to something like
// a Left(BadRequest("error")). Since we're not
// we just wrap the source in a Right(...)
Accumulator.source[ByteString]
.map(Right.apply)
}
def stream = Action(verbatimBodyParser) { implicit request =>
Ok.chunked(request.body)
}
如果你想做一些像转换 TSV 文件的事情,你可以使用 Flow
来转换源,例如:
If you want to do something like transform a TSV file you can use a Flow
to transform the source, e.g:
val tsvToCsv: BodyParser[Source[ByteString, _]] = BodyParser { req =>
val transformFlow: Flow[ByteString, ByteString, NotUsed] = Flow[ByteString]
// Chunk incoming bytes by newlines, truncating them if the lines
// are longer than 1000 bytes...
.via(Framing.delimiter(ByteString("
"), 1000, allowTruncation = true))
// Replace tabs by commas. This is just a silly example and
// you could obviously do something more clever here...
.map(s => ByteString(s.utf8String.split(' ').mkString(",") + "
"))
Accumulator.source[ByteString]
.map(_.via(transformFlow))
.map(Right.apply)
}
def convert = Action(tsvToCsv) { implicit request =>
Ok.chunked(request.body).as("text/csv")
}
Directing the Body 可能会有更多的灵感Play 文档的其他地方部分.
这篇关于Play Framework Scala:如何流式传输请求正文的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!