2013-03-12 10 views
6
  1. Come accedere alle intestazioni di risposta, prima che l'intera risposta sia stata ritrasmessa?
  2. Come si legge lo stream mentre arriva?
  3. HttpClient è la scelta migliore per un controllo granulare della ricezione della risposta http?

Ecco un elemento di cattura che potrebbe illustrare la mia domanda:Leggere intestazioni da HttpResponseMessage prima che il contenuto sia completo al 100%

using (var response = await _httpClient.SendAsync(request, 
    HttpCompletionOption.ResponseHeadersRead)) 
{ 
    var streamTask = response.Content.ReadAsStreamAsync(); 
    //how do I check if headers portion has completed? 
    //Does HttpCompletionOption.ResponseHeadersRead guarantee that? 
    //pseudocode 
    while (!(all headers have been received)) 
    //maybe await a Delay here to let Headers get fully populated 
    access_headers_without_causing_entire_response_to_be_received 

    //how do I access the response, without causing an await until contents downloaded? 
    //pseudocode 
    while (stremTask.Resul.?) //i.e. while something is still streaming 
    //? what goes here? a chunk-read into a buffer? or line-by-line since it's http? 
    ... 


Modifica chiarire un'altra zona grigia per me:
Ogni riferimento ho portato alla luce è una sorta di dichiarazione di blocco, ciò farebbe attendere l'arrivo dei contenuti. I riferimenti a cui leggo di solito accedono a metodi o proprietà sullo streamTask.Result o sul Contenuto e non ne so abbastanza per escludere quali riferimenti siano corretti mentre lo streamTask sta procedendo contro quali causeranno un'attesa fino a quando il compito completato.

+0

ho scritto una risposta, ma poi si rese conto che era un po 'mal studiato e pigro. Invece ho una domanda di follow-up, cosa intendi per dichiarazione di blocco? Tutte le operazioni di HttpClient sono asincrone, non dovrebbe esserci nulla che ti impedisca di leggere le intestazioni e il flusso di contenuti su attività separate, impedendo loro di bloccarsi a vicenda. – Snixtor

+0

@Snixtor, la mia domanda è probabilmente basata su un'assunzione errata, che se attendo esplicitamente o accedo a stremTask.Result, farò leggere l'intero contenuto. In definitiva stavo cercando l'impianto idraulico A) leggere le intestazioni, B) leggere lo stream così com'è Elaborerò la mia domanda con lo pseudo-codice per illustrare che cosa sto immaginando dovrebbe accadere. –

+0

Hai ragione, questa è un'ipotesi errata. 'streamTask.Result' bloccherà fino a quando' Stream' non sarà disponibile, ma non richiederà che l'intero contenuto del flusso sia già stato trasferito. Tecnicamente potrebbero essere disponibili byte di contenuto zero dopo aver chiamato 'streamTask.Result'. – Snixtor

risposta

4

Sulla base dei miei test, il contenuto non verrà trasferito fino a quando non inizierete a leggere il flusso di contenuti, e siete corretti che chiamare Task.Result sia una chiamata bloccante, ma è la sua stessa natura, è un punto di sincronizzazione. Ma, non blocca il pre-buffer dell'intero contenuto, blocca solo fino a quando il contenuto inizia in arrivo dal server.

Quindi un flusso infinito non si bloccherà per una quantità infinita di tempo. Pertanto, provare a recuperare il flusso in modo asincrono potrebbe essere considerato eccessivo, soprattutto se l'operazione di elaborazione dell'intestazione è relativamente breve. Ma se lo desideri, puoi sempre elaborare le intestazioni mentre il flusso di contenuti viene gestito su un'altra attività. Qualcosa del genere lo farebbe.

static void Main(string[] args) 
{ 
    var url = "http://somesite.com/bigdownloadfile.zip"; 
    var client = new HttpClient(); 
    var request = new HttpRequestMessage(HttpMethod.Get, url); 

    var getTask = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); 
    Task contentDownloadTask = null; 

    var continuation = getTask.ContinueWith((t) => 
    { 
     contentDownloadTask = Task.Run(() => 
     { 
      var resultStream = t.Result.Content.ReadAsStreamAsync().Result; 
      resultStream.CopyTo(File.Create("output.dat")); 
     }); 

     Console.WriteLine("Got {0} headers", t.Result.Headers.Count()); 
     Console.WriteLine("Blocking after fetching headers, press any key to continue..."); 
     Console.ReadKey(true); 
    }); 

    continuation.Wait(); 
    contentDownloadTask.Wait(); 
    Console.WriteLine("Finished downloading {0} bytes", new FileInfo("output.dat").Length); 

    Console.WriteLine("Finished, press any key to exit"); 
    Console.ReadKey(true); 
} 

Si noti che non c'è bisogno di controllare se la porzione di intestazioni è completo, hai esplicitamente specificato che con l'opzione HttpCompletionOption.ResponseHeadersRead. L'attività SendAsync non continuerà finché non saranno state recuperate le intestazioni.

+0

Che ne dici di leggere il flusso in blocchi - buffer o stringhe/linee? HttpClient è la classe più appropriata per un controllo così granulare del download di http? –

+1

Per leggere in blocchi puoi usare 'Stream.Read' - http://msdn.microsoft.com/en-us/library/system.io.stream.read.aspx - Anche se avresti bisogno di una situazione abbastanza particolare per giustificarlo (può essere un po 'goffo e lento rispetto a 'CopyTo'.) Se vuoi leggere riga per riga, avvolgi il flusso in un' StreamReader' - http://msdn.microsoft.com/en -us/library/system.io.streamreader.aspx – Snixtor

+1

Come per 'HttpClient', una volta ottenuto il flusso del contenuto della risposta è una sorta di fuori dall'immagine, gestisce la richiesta e la risposta, le intestazioni, alcuni errori di elaborazione, ecc. Non avrai molta più flessibilità di un accesso diretto allo stream di risposta, almeno non nel codice gestito. – Snixtor

3

Il risultato utilizzando le parole chiave attendono/asincroni è ancora più leggibile:

var url = "http://somesite.com/bigdownloadfile.zip"; 

using (var httpClient = new HttpClient()) 
using (var httpRequest = new HttpRequestMessage(HttpMethod.Get, url)) 
using(HttpResponseMessage response = await httpClient.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead)) 
using (Stream stream = await response.Content.ReadAsStreamAsync()) 
{ 
    //Access to the Stream object as it comes, buffer it or do whatever you need 
}  
Problemi correlati