How to copy HttpContent async and cancelable?

走远了吗. 提交于 2019-12-29 06:49:14

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!