根据 Yann Simon 的回答,这是一个简单的 CORS 代理实现,它允许流式传输下载的远程文件并将它们流式传输到客户端。它不会加载内存中的所有文件。
import play.api.libs.iteratee._
private def getAndForwardStream(requestHolder: WSRequestHolder)(computeHeaders: ResponseHeaders => ResponseHeader): Future[SimpleResult] = {
val resultPromise = scala.concurrent.Promise[SimpleResult]
requestHolder.get { wsResponseHeaders: ResponseHeaders =>
val (wsResponseIteratee, wsResponseEnumerator) = Concurrent.joined[Array[Byte]]
val result = SimpleResult(
header = computeHeaders(wsResponseHeaders),
body = wsResponseEnumerator
)
resultPromise.success(result)
wsResponseIteratee
}
resultPromise.future
}
def corsProxy(url: URL) = Action.async { implicit request =>
val requestHolder = WS.url(url.toString).withRequestTimeout(10000)
getAndForwardStream(requestHolder) { wsResponseHeaders: ResponseHeaders =>
// We use the WS response headers and transmit them unchanged to the client, except we add the CORS header...
val originToAllow = request.headers.get("Origin").getOrElse("*")
val headers = wsResponseHeaders.headers.mapValues(_.head) + ("Access-Control-Allow-Origin" -> originToAllow)
ResponseHeader(
status = wsResponseHeaders.status,
headers = headers
)
}
}
这里的重要部分是使用play.api.libs.iteratee.Concurrent.joined[Array[Byte]]
. 它允许创建一个 Iteratee/Enumerator 对,以便每当您向 Iteratee 添加字节时,这些字节将由枚举器枚举。
这是缺少的部分,因为:
- 您需要一个 Iteratee 来使用 WS 响应。
- 您需要一个 Enumerator 来生成播放框架响应。