2013-05-21 64 views
10

我需要將http post請求中的大量數據發送到支持gziped編碼請求的服務器。如何即時壓縮http請求,而不需要在內存中加載壓縮緩衝區

從一個簡單的

public async Task<string> DoPost(HttpContent content) 
{ 
    HttpClient client = new HttpClient(); 
    HttpResponseMessage response = await client.PostAsync("http://myUri", content); 

    response.EnsureSuccessStatusCode(); 
    return await response.Content.ReadAsStringAsync(); 
} 

開始我只是增加了一個預壓縮

public async Task<string> DoPost(HttpContent content, bool compress) 
{ 
    if (compress) 
    content= await CompressAsync(content); 

    return await DoPost(content); 
} 

private static async Task<StreamContent> CompressAsync(HttpContent content) 
{ 
    MemoryStream ms = new MemoryStream(); 
    using (GZipStream gzipStream = new GZipStream(ms, CompressionMode.Compress, true)) 
    { 
    await content.CopyToAsync(gzipStream); 
    await gzipStream.FlushAsync(); 
    } 

    ms.Position = 0; 
    StreamContent compressedStreamContent = new StreamContent(ms); 
    compressedStreamContent.Headers.ContentType = content.Headers.ContentType; 
    compressedStreamContent.Headers.Add("Content-Encoding", "gzip"); 

    return compressedStreamContent; 
} 

它完美,但壓縮數據被完全地加載到內存中發送請求之前。 I 希望能夠在流式傳輸過程中實時壓縮數據。

要做到這一點,我已經試過下面的代碼:

private static async Task<HttpContent> CompressAsync2(HttpContent content) 
{ 
    PushStreamContent pushStreamContent = new PushStreamContent(async (stream, content2, transport) => 
    { 
    using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Compress, true)) 
    { 
     try 
     { 
     await content.CopyToAsync(gzipStream); 
     await gzipStream.FlushAsync(); 
     } 
     catch (Exception exception) 
     { 
     throw; 
     } 
    } 
    }); 
    pushStreamContent.Headers.ContentType = content.Headers.ContentType; 
    pushStreamContent.Headers.Add("Content-Encoding", "gzip"); 

    return pushStreamContent; 
} 

但它永遠不會熄滅CopyToAsync(gzipStream)的。 FlushAsync永遠不會執行,也不會引發異常,Fiddler也不會看到任何已啓動的帖子。

我的問題是:

  • 爲什麼CompressAsync2不起作用?
  • 如何在發送過程中進行壓縮以及是否在內存中加載壓縮緩衝區?

任何幫助將不勝感激。

+1

'PushStreamContent'不支持'async' lambda。 –

+0

@Stephen Cleary:你說得對,我應該檢查一下!我無法從PushStreamContent派生到重載SerializeToStreamAsync(太多內部)。你能看到一個解決方案嗎? – MuiBienCarlota

+0

Humm,只需要[PushStreamContent並修改它](https://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Net.Http.Formatting/PushStreamContent.cs)來支持'異步'lambda。幾個月前,我一直在我的「待辦事項」名單上,但沒有得到它。 –

回答

12

嘗試使用從WebAPIContrib https://github.com/WebApiContrib/WebAPIContrib/blob/master/src/WebApiContrib/Content/CompressedContent.cs

public async Task<string> DoPost(HttpContent content) 
{ 
    HttpClient client = new HttpClient(); 
    HttpResponseMessage response = await client.PostAsync("http://myUri", new CompressedContent(content,"gzip")); 

    response.EnsureSuccessStatusCode(); 
    return await response.Content.ReadAsStringAsync(); 
} 

P.S.的CompressedContent類這隻會在.net 4.5上傳輸內容。 .net 4版本的HttpWebRequest總是緩衝發送的內容。

P.P.S.爲每個請求創建一個新的HttpClient不是使用HttpClient的最佳方式。這樣做會強制爲每個請求創建一個新的TCP連接。

+0

真正的Interresting鏈接:非常感謝。我需要繼續使用.Net 4.0(需要XP)。即使我爲.Net 4.0編譯安裝.Net 4.5,你是否認爲我可以獲得流式傳輸? – MuiBienCarlota

+0

我創建了一個本地HttpClient,只是爲了簡化問題。 – MuiBienCarlota

+0

@MuiBienCarlota我不確定。我的猜測是肯定的,但你需要測試。關於HttpClient ...很酷,我只是看到它很多,我正在執行一項任務,以取消它;-) –