2012-10-20 12 views
6

バイナリファイルの内容を示すWebサービスを呼び出す必要があります。再生:バイナリWebサービスの応答

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

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

大きなファイルには良い方法ではありません。約Streaming HTTP responsesですが、Playフレームワークの初心者にとって私にとっては複雑なようです。

答えて

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)) 
    }) 
} 
+0

ありがとう、 'f.ahcResponse.getResponseBodyAsBytes'の両方のソリューションはバイナリデータでも動作します。ストリーミング機能は私が思ったよりも簡単だと思われます:-) – Sonson123

+3

これはブロックしませんか? InputStreamsは通常は.... –

+0

実際には読み込み時にブロックされませんが、メモリ内に読み込まれているためです。これを避けるには、消費関数の引数をとる 'get()'のオーバーロードされた形式を使用する必要があります: 'get [A](consumer:(ResponseHeaders)⇒Iteratee [Array [Byte]、A])' –

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 
} 
+0

ありがとう、それは仕事をするために "参加"の許可を受けているようです –

1

を、ここからダウンロードリモートファイルをストリーミングし、それらをストリーミングするために許可する簡単な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を必要

  • :ので

    これが欠けていました。

  • 再生フレームワークの応答を生成するには、列挙子が必要です。
+0

遊びに来る新しいAPIストリーム()が通知されます: http://www.playframework.com/documentation/2.3.x/api/scala/ index.html#play.api.libs.ws.WSRequestHolder これで、あなたは[[]に参加する必要はありません –

+0

thanks @YannSimon thx;) –

関連する問題