问题
I'm using HttpClient.PostAsync()
and the response is an HttpResponseMessage
. Its Content property is of type HttpContent
which has a CopyToAsync()
method. Unfortunately, this is not cancelable.
Is there a way to get the response copied into a Stream
and pass a CancellationToken
?
I am not stuck with CopyToAsync()
! If there is a workaround, that would be fine. Like read a couple of bytes, check if canceled, continue reading and so on.
The HttpContent.CreateContentReadStreamAsync()
methods looks like it would be a candidate. Unfortunately, it's not available with my selected profile. Also unclear if it would read all data in one go and waste a lot of memory.
Note: I'm using this inside a PCL targeting WP8, Windows Store 8, .NET 4.5, Xamarin.iOS and Xamarin.Android
回答1:
I believe this should work:
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);
}
}
Note that ReadAsStreamAsync
calls CreateContentReadStreamAsync
, which for stream responses just returns the underlying content stream without buffering it into memory.
回答2:
You can't cancel a non cancellable operation. See How do I cancel non-cancelable async operations?.
You can however allow your code flow to behave as if the underlying operation was canceled, with WithCancellation
.
public static Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken)
{
return task.IsCompleted
? task
: task.ContinueWith(
completedTask => completedTask.GetAwaiter().GetResult(),
cancellationToken,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
}
Usage:
await httpContent.PostAsync(stream).WithCancellation(new CancellationTokenSource(1000).Token);
回答3:
HttpClient.CancelPendingRequests is expected to cancel all pending operations. I haven't verified though if that applies to CopyToAsync
too. Feel free to try it:
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);
}
}
[UPDATE] Verified: unfortunately, this doesn't cancel HttpContent.CopyToAsync
. I'm keeping this answer though, as the pattern itself may be useful for cancelling other operations on HttpClient
.
来源:https://stackoverflow.com/questions/20902488/how-to-copy-httpcontent-async-and-cancelable