2014-03-06 41 views
5

TL; DR:如何流使用WCF已知大小的大文件,還是出現進展(Content-Length)到最終用戶(網頁瀏覽器)?Transfering大文件:合併流傳輸和內容長度

我有一個WCF REST服務,下載,然後非常大的文件(1-20Gb)用於Web瀏覽器。爲了簡化,請將我的服務視爲代理。這迫使我在綁定上設置TransferMode = StreamedTransferMode = StreamedResponse,或者在實際下載開始之前,最終客戶端將不得不等待源文件下載到網絡服務器。此外,緩衝傳輸模式會導致服務器死機(RAM使用率)。中級磁盤存儲不是一個選項。從TransferMode手冊頁:

(...)緩衝傳輸的內存緩衝區存放整個消息,直到 傳輸完成。

但設置TransferModeStreamedStreamedResponse時,WCF不再返回頭Content-length到客戶端,和一個新的頭Transfer-Encoding: chunked添加。這是符合wikipedias article上分塊傳輸:

(...)使用傳輸編碼HTTP標頭中的Content-Length頭 (...)

的地方但我總是知道事先要傳輸的數據的大小,對於最終用戶來說,不知道下載的大小是非常令人沮喪的。所以:

(如何)我可以配置WCF綁定使用「流」傳輸模式(更具體地說,緩衝整個郵件發送前),仍然使用Content-Length頭?

一些線索:

  • This q/a指出HTTP標準不允許在同一消息中都Transfer-EncodingContent-length: 123456,所以我想這是不是一種選擇?

  • 我曾嘗試在IDispatchMessage.BeforeSendReply,但在這一點上Content-Length頭還沒有被刪除修改使用的檢查頭,並Transfer-Encoding尚未確定,所以它是「太早」。我後來讀到了分塊傳輸編碼是在T​​CP層面上的,所以在這一點上改變標題可能不會工作,即使我可以。

  • 我試過設置aspNetCompatibilityEnabled="true",將wcf傳輸模式設置爲緩衝輸出(默認),然後設置System.Web.HttpContext.Current.Response.BufferOutput = false;。 wcf忽略了這個消息,但是這個消息被明確地緩衝了。

  • 這似乎是缺少功能,根據this link。但是在某個地方可能還有一個古怪的解決方法。

回答

2

這是一個測試解決方案,只適用於WCF IIS下 - 我還沒有找到一個自我的任何解決方案託管服務。

總之 - 打開aspNetCompatibility,讓你運行時System.Web.HttpContext.Current訪問。

Web.config

(...) 
    <system.serviceModel> 
     <bindings> 
      <webHttpBinding> 
       <binding transferMode="Streamed"> 
       </binding> 
      </webHttpBinding> 
     </bindings> 
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" /> 
(...) 
</system.serviceModel> 

,並在您的服務功能,它返回一個流:

HttpContext.Current.Response.Headers.Add("Content-Length", 
contentLength.ToString()); 

任何像下面將被忽略:

WebOperationContext.Current.OutgoingResponse.Headers["Content-Length"] = 
     contentLength.ToString(); 

就這麼簡單! Creds去Uffe Lausen's question on Msdn

0

如果你有過客戶端代碼的控制,你可以寫數據長度爲響應流中的第一個字節,這是我要做的事來傳輸大型文件,但客戶必須知道第一字節只是大小,不包含任何數據。

例@server:

public Stream GetBigData() 
{ 
    byte[] bigData = GetBigDataFromSomeWhere(); 
    var ms = new MemoryStream(); 
    var lengthInfo = BitConverter.GetBytes(bigData.Length); 
    ms.Write(lengthInfo, 0, lengthInfo.Length); 
    ms.Write(bigData , 0, bigData .Length); 
    ms.Flush(); 
    ms.Position = 0; 
    return ms; 
} 

例@client:

using (var respStream = yourService.GetBigData()) 
using (var resultStream = new MemoryStream()) 
{ 
    //read first 4 bytes 
    var lengthBuffer = new byte[4]; 
    respStream.Read(lengthBuffer, 0, 4); 
    var totalCount = BitConverter.ToInt32(lengthBuffer, 0); 
    resultStream.Capacity = totalCount; 

    //read rest 
    var readcount = 0; 
    var buffer = new byte[1024 * 32]; 
    while ((readcount = respStream.Read(buffer, 0, buffer.Length)) > 0) 
    { 
     resultStream.Write(buffer, 0, readcount); 
     UpdateProgress(readcount, totalCount) 
    } 
} 
+0

不幸的是,在這個問題說(我會有點強調這使得它更清楚),客戶端是Web瀏覽器。因此,客戶端的任何解決方法都必須使用JavaScript或任何其他瀏覽器可用的技巧。雖然如果有人對我的問題回答「不」,我可能不得不考慮自定義下載器,這可能看起來像您的答案。 – Tewr