BodyParser.Raw 在 Play 中的表现如何!框架工作?

问题描述

我对 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 状态。