Come copiare HttpContent async e cancellabile?

Sto usando HttpClient.PostAsync() e la risposta è HttpResponseMessage . La sua proprietà Content è di tipo HttpContent che ha un metodo CopyToAsync() . Sfortunatamente, questo non è cancellabile. C’è un modo per ottenere la risposta copiata in un Stream e passare un CancellationToken ?

Non sono bloccato con CopyToAsync() ! Se c’è una soluzione, andrebbe bene. Come leggere un paio di byte, controllare se cancellato, continuare a leggere e così via.

I metodi HttpContent.CreateContentReadStreamAsync() sembra un candidato. Purtroppo, non è disponibile con il mio profilo selezionato. Inoltre non è chiaro se leggerà tutti i dati in una volta sola e sprecherà molta memoria.

Nota: lo sto utilizzando all’interno di un PCL con targeting per WP8, Windows Store 8, .NET 4.5, Xamarin.iOS e Xamarin.Android

Credo che questo dovrebbe funzionare:

 public static async Task DownloadToStreamAsync(string uri, HttpContent data, Stream target, CancellationToken token) { using (var client = new HttpClient()) using (var response = await client.PostAsync(uri, data, token)) using (var stream = await response.Content.ReadAsStreamAsync()) { await stream.CopyToAsync(target, 4096, token); } } 

Nota che ReadAsStreamAsync chiama CreateContentReadStreamAsync , che per le risposte allo stream restituisce semplicemente il stream di contenuti sottostante senza memorizzarlo nella memoria.

Non è ansible annullare un’operazione non cancellabile. Vedi Come annullare le operazioni asincrone non cancellabili? .

Puoi tuttavia consentire al stream del codice di comportarsi come se l’operazione sottostante fosse stata annullata, con WithCancellation .

 public static Task WithCancellation(this Task task, CancellationToken cancellationToken) { return task.IsCompleted ? task : task.ContinueWith( completedTask => completedTask.GetAwaiter().GetResult(), cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); } 

Uso:

 await httpContent.PostAsync(stream).WithCancellation(new CancellationTokenSource(1000).Token); 

HttpClient.CancelPendingRequests prevede che HttpClient.CancelPendingRequests annulli tutte le operazioni in sospeso. Non ho verificato se questo vale anche per CopyToAsync . Sentiti libero di provarlo:

 public static async Task CopyToAsync( System.Net.Http.HttpClient httpClient, System.Net.Http.HttpContent httpContent, Stream stream, CancellationToken ct) { using (ct.Register(() => httpClient.CancelPendingRequests())) { await httpContent.CopyToAsync(stream); } } 

[AGGIORNAMENTO] Verificato: sfortunatamente, questo non annulla HttpContent.CopyToAsync . Sto mantenendo questa risposta, poiché il pattern stesso potrebbe essere utile per cancellare altre operazioni su HttpClient .