You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Play Framework 2.5(Scala)请求流无法转为分块响应输出问题

问题分析与解决方案

你的问题核心在于请求体流与响应流的生命周期冲突:直接返回请求体的原始Source时,它和请求的输入流强绑定,当请求体接收完成后,底层输入流会被Play的HTTP服务器关闭,导致响应流无法正确完成数据传输,最终引发curl的transfer closed with outstanding read data remaining错误。而自定义Source能正常工作,是因为它完全独立于请求生命周期,流的创建、发送和终止都在可控范围内。

下面是具体的解决方案和解释:

一、使用Play内置的流式BodyParser(推荐)

Play提供了parse.verbatim这个专门为流式处理设计的BodyParser,它会正确管理请求体流的生命周期,避免直接使用Accumulator.source带来的冲突。

实现代码

import play.api.mvc._
import akka.util.ByteString

def endpointA = Action(parse.verbatim) { request =>
  // 保持响应的Content-Type与请求一致,避免类型不匹配问题
  val contentType = request.headers.get("Content-Type").getOrElse("text/plain")
  Ok.chunked(request.body).as(contentType)
}

如果必须使用EssentialAction:

def endpointA = EssentialAction { requestHeader =>
  val parser = BodyParsers.parse.verbatim
  parser(requestHeader).map { parsedBody =>
    val contentType = requestHeader.headers.get("Content-Type").getOrElse("text/plain")
    Ok.chunked(parsedBody).as(contentType)
  }
}

二、解决Keep-Alive连接的兼容性问题

你设置了Connection: Keep-Alive,但Play在流式响应完成后,可能没有正确维持长连接的状态,导致curl误以为还有未传输的数据。可以尝试两种方式:

  1. 临时关闭Keep-Alive:测试时去掉Connection和Keep-Alive头,看是否正常:
curl -v --data 'hello' -H "Content-type: text/plain" http://localhost:9584/binding-tariff-admin/upload-csv
  1. 强制响应关闭连接:如果必须保留Keep-Alive,但仍报错,可以在响应中显式添加Connection: close头:
Ok.chunked(request.body).as(contentType).withHeaders("Connection" -> "close")

三、处理流的背压与数据传递

如果以上方案仍有问题,可以在请求体流上添加简单的转换,确保数据被正确传递到响应流(比如复制ByteString,避免原始流的引用问题):

def endpointA = Action(parse.verbatim) { request =>
  val safeSource = request.body.map(bs => ByteString(bs.toArray))
  val contentType = request.headers.get("Content-Type").getOrElse("text/plain")
  Ok.chunked(safeSource).as(contentType)
}

四、检查Play版本

确保你使用的是Play 2.5.x的最新版本(比如2.5.18),早期的2.5版本存在一些流式处理的bug,升级后可能直接解决问题。

内容的提问来源于stack exchange,提问作者nfvindaloo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 18:08:10