BodyParser.Raw 怎么玩!框架工作?

how does BodyParser.Raw in Play! framework work?

我对 Play 中的 BodyParser.Raw 有疑问!框架。 The official document 说:

Parses the body as a RawBuffer. This will attempt to store the body in memory, up to Play’s configured memory buffer size, but fallback to writing it out to a File if that’s exceeded.

我很难理解上面的描述。这是否意味着以下 -

如果我将以下配置放入我的 application.conf 文件中:

play.http.parser.maxMemoryBuffer=128k
play.http.parser.maxDiskBuffer=1G

并说我的请求 body 大小为 15MB,然后播放!将 read/parse 来自请求 body 的前 128k,将其写入文件,然后 stream/parse 来自请求 body 的下一个 128k,将其写入同一个文件, ...,直到 15MB 的请求 body 被完全解析?这意味着我们只使用了 128k 内存,并且我们能够处理大小为 15MB 的请求 body??

我知道这听起来好得令人难以置信...

有人可以解释一下吗?

更新:

如果有人在这里遇到同样的问题,我想在这里添加一些更新。所以@Ivan Kurchenko 是对的(见下面他的回答):BodyParser.Raw 可用于 POST 比配置的 play.http.parser.maxMemoryBuffer 大的请求 body。玩!在这种情况下,将使用磁盘缓冲请求。换句话说,您可以 POST 一个比 JVM 中的可用内存更大的大请求 body。

我使用 Chrome(以及 Firefox)测试失败的原因是我默认启用了 CSRF 过滤器。一旦我禁用它,一切正常。比如下面的配置,

play.http.parser.maxMemoryBuffer=1k
play.http.parser.maxDiskBuffer=4G
play.filters.disabled+=play.filters.csrf.CSRFFilter

表示如果请求 body 大于 1k,播放!将使用您的磁盘来缓冲您的请求(当然,我禁用了 CSRF 过滤器)。现在,通过这个配置,我可以成功上传超过3G的电影。所以确实,如果你需要处理一个大的请求,可以使用 BodyParser.Raw。 HTH!

不是真的 - 如果请求大小超过内存阈值,在您配置为 play.http.parser.maxMemoryBuffer=128k 的情况下,整个请求正文将写入临时文件或写入内存。然后下一个解析体也将从文件或内存中流式传输。 一起来看看:

BodyParser.raw - creates RawBuffer inside, like for instance here 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 - which on its own side read bytes if in memory buffer exists. Or asFile implementation 从临时文件中读取字节。

另请注意:如果传入请求正文大小超过 play.http.parser.maxDiskBuffer 中的配置值,Play 将响应 413 - REQUEST_ENTITY_TOO_LARGE 状态。