2012-10-20 30 views
6

Tôi phải gọi một dịch vụ web cung cấp cho tôi nội dung của tệp nhị phân. Tôi chỉ muốn cung cấp nội dung tương tự cho người gọi của bộ điều khiển của tôi:Phát: Phản hồi webservice nhị phân

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

Thao tác này cho tệp văn bản, nhưng tệp nhị phân sẽ bị hỏng. Những gì tôi đang làm sai ở đây? (Có lẽ đó là số f.body mã hóa kết quả nhị phân từ webservice thành Chuỗi? Nhưng làm cách nào tôi có thể lấy dữ liệu thô?)

Tôi biết, đó không phải là cách tốt cho tệp lớn - tôi đã đọc trong tài liệu Play khoảng Streaming HTTP responses, nhưng có vẻ phức tạp đối với tôi như một người mới bắt đầu với khung chơi.

Trả lời

8

Bạn có thể lấy dữ liệu thô bằng cách sử dụng f.ahcResponse.gerResponseBodyAsBytes. Nhưng tôi nghĩ, điều này sẽ tải toàn bộ phản hồi vào bộ nhớ, điều đó không hiệu quả.

Bạn có thể sử dụng chức năng phát trực tuyến Play! cung cấp khá dễ dàng như thế này:

Async { 
    WS.url("http://url/to/webservice/file.txt").get().map(response => { 
    val asStream: InputStream = response.ahcResponse.getResponseBodyAsStream 
    Ok.stream(Enumerator.fromStream(asStream)) 
    }) 
} 
+0

Cảm ơn bạn rất nhiều, cả hai giải pháp với công việc 'f.ahcResponse.getResponseBodyAsBytes' giờ đây cũng có dữ liệu nhị phân. Các chức năng streaming dường như dễ dàng hơn tôi đã nghĩ ... :-) – Sonson123

+3

Điều này không chặn? InputStreams thường làm .... –

+0

Trong thực tế nó không chặn khi bạn đọc từ nó ... nhưng chỉ vì nó được đọc tất cả vào bộ nhớ đầu tiên. Để tránh điều đó, bạn sẽ phải sử dụng dạng 'get()' quá tải lấy đối số hàm người tiêu dùng: 'get [A] (consumer: (ResponseHeaders) ⇒ Iteratee [Array [Byte], A])' –

3

Nếu bạn muốn dòng nội dung:

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

cảm ơn rất nhiều, có vẻ như giấy phép "đã tham gia" để thực hiện công việc –

1

Dựa trên Yann Simon câu trả lời, đây là một đơn giản CORS thực hiện ủy quyền cho phép stream tải về tập tin từ xa và dòng họ cho khách hàng. Nó không tải tất cả các tập tin trong bộ nhớ.

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

Điều quan trọng ở đây là sử dụng play.api.libs.iteratee.Concurrent.joined[Array[Byte]]. Nó cho phép tạo cặp Iteratee/Enumerator để bất cứ khi nào bạn thêm byte vào Iteratee, các byte này sẽ được liệt kê bởi điều tra viên.

Đây là mảnh mất tích vì:

  • Bạn cần một Iteratee để tiêu thụ đáp ứng WS.
  • Bạn cần một ĐTV để tạo ra phản hồi khung chơi.
+0

thông báo luồng API mới() đến với phiên bản 2.3: http://www.playframework.com/documentation/2.3.x/api/scala/ index.html # play.api.libs.ws.WSRequestHolder với nó, bạn không cần tham gia [] nữa –

+0

cảm ơn @YannSimon thx;) –

Các vấn đề liên quan