2013-05-21 8 views
10

Ho bisogno di inviare dati voluminosi in una richiesta di post http a un server che supporta richieste codificate gziped.Come comprimere la richiesta HTTP al volo e senza caricare il buffer compresso in memoria

Partendo da un semplice

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

Ho appena aggiunto un pre compressione

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

funziona perfettamente, ma comprimono i dati sono completamente caricati in memoria prima di inviare richiesta. I vorrebbe essere in grado di comprimere i dati al volo durante l'invio in streaming.

Per farlo, ho provato seguente codice:

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

ma non passa mai di copyToAsync (GZipStream). FlushAsync non viene mai eseguito e non viene generata alcuna eccezione e Fiddler non vede alcun post avviato.

Le mie domande sono:

  • Perché CompressAsync2 non funziona?
  • Come comprimere al volo durante l'invio e senza caricare il buffer compresso in memoria?

Qualsiasi aiuto sarebbe molto apprezzato.

+1

'PushStreamContent' non supporta (attualmente)' asd' lambda. –

+0

@Stephen Cleary: hai ragione, avrei dovuto controllare! E non posso derivare da PushStreamContent per sovraccaricare SerializeToStreamAsync (troppi solo interni). riesci a vedere una soluzione? – MuiBienCarlota

+0

Humm, potrebbe essere più semplice prendere ['PushStreamContent' e modificarlo] (https://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Net.Http.Formatting/PushStreamContent.cs) per supportare' async' lambda. È stato nella mia lista di "cose ​​da fare" da alcuni mesi, ma non ci sono riuscito. –

risposta

12

Provare a utilizzare la classe CompressedContent da 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. che questo trasmetterà solo il contenuto su .net 4.5. La versione .net 4 di HttpWebRequest memorizza sempre i contenuti inviati.

P.P.S. La creazione di un nuovo HttpClient per ogni richiesta non è il modo migliore per utilizzare HttpClient. In questo modo verrà forzata la creazione di una nuova connessione TCP per ogni richiesta.

+0

Link davvero interessante: grazie mille. Devo rimanere su .Net 4.0 (necessario XP). Pensi che io possa essere trasmesso in streaming quando .Net 4.5 è installato anche se compilo per .Net 4.0? – MuiBienCarlota

+0

Ho creato un HttpClient locale solo per la semplicità delle domande. – MuiBienCarlota

+0

@MuiBienCarlota Non ne sono sicuro. La mia ipotesi sarebbe sì, ma è necessario testare. Per quanto riguarda HttpClient ... cool, lo vedo molto e sono in missione per eliminarlo ;-) –

Problemi correlati