问题描述
我对 Play 中的 BodyParser.Raw
有疑问!框架。 The official document 说:
将正文解析为 RawBuffer
。这将尝试存储身体
在内存中,最多可达 Play 配置的内存缓冲区大小,但回退到
如果超出,则将其写入文件。
我很难理解上面的描述。这是否意味着以下 -
如果我将以下配置放入我的 application.conf
文件:
play.http.parser.maxMemoryBuffer=128k
play.http.parser.maxdiskBuffer=1G
并说我的请求正文的大小为 15MB,然后播放!将从请求正文中读取/解析前 128k,将其写入文件,然后从请求正文中流式传输/解析下一个 128k,将其写入同一个文件,...,直到 15MB 请求正文被完全解析?这意味着我们只使用了 128k 内存,并且我们能够处理大小为 15MB 的请求体??
我知道这听起来好得令人难以置信...
有人能解释一下吗?
更新:
如果有人在这里遇到同样的问题,我想在这里添加一些更新。所以@Ivan Kurchenko 是对的(见下面他的回答):BodyParser.Raw
可用于 POST
大于配置的 play.http.parser.maxMemoryBuffer
的请求正文。玩!在这种情况下,将使用磁盘缓冲请求。换句话说,您可以POST
一个比 JVM 中的可用内存更大的大型请求正文。
我使用 Chrome(以及 Firefox)测试失败的原因是因为我默认启用了 CSRF 过滤器。一旦我禁用它,一切正常。比如下面的配置,
play.http.parser.maxMemoryBuffer=1k
play.http.parser.maxdiskBuffer=4G
play.filters.disabled+=play.filters.csrf.CSRFFilter
说如果请求正文大于 1k,播放!将使用您的磁盘来缓冲您的请求(当然,我禁用了 CSRF 过滤器)。现在,通过这个配置,我可以成功上传超过3G大小的电影。因此,确实,如果您需要处理大型请求,可以使用 BodyParser.Raw
。 HTH!
解决方法
不是真的 - 如果请求大小超过内存阈值,在您的情况下配置为 play.http.parser.maxMemoryBuffer=128k
整个请求正文将写入临时文件或写入内存,否则。然后下一个解析体也将从文件或内存中流式传输。
一起来看看:
BodyParser.raw - 在内部创建 RawBuffer
,例如这里 https://github.com/playframework/playframework/blob/6d0789468909d5d7bdabc6c4207337bd7a7ca9b1/core/play/src/main/scala/play/api/mvc/BodyParsers.scala#L612
在 RawBuffer
里面有两个我们感兴趣的方法:
push method implementation - 接受传入字节并且总大小超过缓冲区大小执行 backToTemporaryFile
- 表示将所有请求写入临时文件。
@volatile private var inMemory: ByteString = initialData
@volatile private var backedByTemporaryFile: TemporaryFile = _
@volatile private var outStream: OutputStream = _
private[play] def push(chunk: ByteString): Unit = {
if (inMemory != null) { //checks whether current in memory buffer exists
//if next readed chunk of request body exceeds in memory buffer size
if (chunk.length + inMemory.size > memoryThreshold) {
backToTemporaryFile() // create temporary file
outStream.write(chunk.toArray)// write buffer to temporary file
} else {
inMemory = inMemory ++ chunk // append in memory buffer with next chunk
}
} else {
outStream.write(chunk.toArray) // append in memory buffer with next chunk
}
}
然后是 asBytes method implementation - 如果内存缓冲区存在,它会在它自己的一侧读取字节。或者 asFile implementation 从临时文件中读取字节。
另请注意:如果传入的请求正文大小超过 play.http.parser.maxDiskBuffer
中的配置值,Play 将响应 413 - REQUEST_ENTITY_TOO_LARGE 状态。