6

バイナリ ファイルのコンテンツを提供する Web サービスを呼び出す必要があります。コントローラーの呼び出し元に同じコンテンツを返したいだけです。

val blobPromise = WS.url("http://url/to/webservice/file.txt").get()
Async {
  blobPromise.map(f => Ok(f.body))
}

これはテキスト ファイルでは機能しますが、バイナリ ファイルは破損します。ここで何が間違っていますか?(おそらくf.body、Web サービスからのバイナリ結果を文字列にエンコードするのは ですか?しかし、どうすれば生データを取得できますか?)

私は知っています、それは大きなファイルには良い方法ではありません - 私はHTTP 応答のストリーミングに関する Play ドキュメントを読みましたが、Play フレームワークの初心者としては複雑に思えます。

4

3 に答える 3

8

を使用して生データを取得できますf.ahcResponse.gerResponseBodyAsBytes。しかし、これは応答全体をメモリにロードするため、非効率的だと思います。

Play! のストリーミング機能を利用できます。次のように非常に簡単に提供します。

Async {
  WS.url("http://url/to/webservice/file.txt").get().map(response => {
    val asStream: InputStream = response.ahcResponse.getResponseBodyAsStream
    Ok.stream(Enumerator.fromStream(asStream))
  })
}
于 2012-10-20T21:07:57.180 に答える
3

コンテンツをストリーミングしたい場合:

def streamFromWS = Action.async { request =>
  import play.api.libs.iteratee.Concurrent.joined

  val resultPromise = Promise[SimpleResult]

  val consumer = { rs: ResponseHeaders =>
    val (wsConsumer, stream) = joined[Array[Byte]]
    val contentLength = rs.headers.get("Content-Length").map(_.head).get
    val contentType = rs.headers.get("Content-Type").map(_.head).getOrElse("binary/octet-stream")
    resultPromise.success(
      SimpleResult(
        header = ResponseHeader(
          status = OK,
          headers = Map(
            CONTENT_LENGTH -> contentLength,
            CONTENT_DISPOSITION -> s"""attachment; filename="file.txt"""",
            CONTENT_TYPE -> contentType
          )),
        body = stream
      ))
    wsConsumer
  }

  WS.url("http://url/to/webservice/file.txt").get(consumer).map(_.run)

  resultPromise.future
}
于 2014-02-18T12:08:39.360 に答える
1

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 にバイトを追加するたびに、これらのバイトが列挙子によって列挙子になるようにすることができます。

次の理由により、これは欠落していました。

  • WS 応答を使用するには Iteratee が必要です。
  • play フレームワークのレスポンスを生成するには列挙子が必要です。
于 2014-06-10T13:46:50.267 に答える