Met*_*urf 0 c# webclient cancellation
寻找一种更普遍接受的等待模式WebClient:
限制条件:
Task.Run(async () => await method())Download调用该方法时,它只需要像普通方法一样返回字符串WebClient.DownloadFileTaskAsync或 都没有区别;DownloadFileAsync只需能够根据需要使用取消下载WebClient当前的实现似乎有效,但似乎不太正确。是否有比使用while循环并在使用时Thread.Sleep定期检查更普遍可接受的替代方案?otherObject.ShouldCancelWebClient
private string Download(string url)
{
// setup work
string fileName = GenerateFileName();
// download file
using (var wc = new WebClient())
{
wc.DownloadFileCompleted += OnDownloadCompleted
Task task = wc.DownloadFileTaskAsync(url, fileName);
// Need to wait until either the download is completed
// or download is canceled before doing any other work
while (wc.IsBusy || task.Status == TaskStatus.WaitingForActivation)
{
if (otherObject.ShouldCancel)
{
wc.CancelAsync();
break;
}
Thread.Sleep(100);
}
void OnDownloadCompleted(object obj, AsyncCompletedEventArgs args)
{
if(args.Cancelled)
{
// misc work
return;
}
// misc work (different than other work below)
}
}
// Other work after downloading, regardless of cancellation.
// Could include in OnDownloadCompleted as long as this
// method blocked until all work was complete
return fileName;
}
Run Code Online (Sandbox Code Playgroud)
小智 5
我希望这会有所帮助。基本上你的包装器使用cancelToken.Register(webClient.Cancel);注册一个回调。一旦调用cancelToken.Cancel(),异步任务应该抛出一个异常,您可以按如下方式处理该异常:
public class Client
{
public async Task<string> DownloadFileAsync(string url, string outputFileName, CancellationToken cancellationToken)
{
using (var webClient = new WebClient())
{
cancellationToken.Register(webClient.CancelAsync);
try
{
var task = webClient.DownloadFileTaskAsync(url, outputFileName);
await task; // This line throws an exception when cancellationTokenSource.Cancel() is called.
}
catch (WebException ex) when (ex.Status == WebExceptionStatus.RequestCanceled)
{
throw new OperationCanceledException();
}
catch (AggregateException ex) when (ex.InnerException is WebException exWeb && exWeb.Status == WebExceptionStatus.RequestCanceled)
{
throw new OperationCanceledException();
}
catch (TaskCanceledException)
{
throw new OperationCanceledException();
}
return outputFileName;
}
}
}
Run Code Online (Sandbox Code Playgroud)
尝试此示例的简单方法
private async static void DownloadFile()
{
var cancellationTokenSource = new CancellationTokenSource();
var client = new Client();
var task = client.DownloadFileAsync("url",
"output.exe", cancellationTokenSource.Token);
cancellationTokenSource.Token.WaitHandle.WaitOne(TimeSpan.FromSeconds(5));
cancellationTokenSource.Cancel();
try
{
var result = await task;
}
catch (OperationCanceledException)
{
// Operation Canceled
}
}
Run Code Online (Sandbox Code Playgroud)
在更现实的场景中,cancelTokenSource.Cancel() 将由由于用户交互或回调而引发的事件调用。
更新
另一种方法是订阅 DownloadProgressChanged 事件并在调用回调时检查 otherObject.ShouldCancel。
这是一个例子:
public class Client
{
public string Download(string url)
{
// setup work
string fileName = GenerateFileName();
// download file
using (var wc = new WebClient())
{
wc.DownloadProgressChanged += OnDownloadProgressChanged;
wc.DownloadFileCompleted += OnDownloadFileCompleted;
DownloadResult downloadResult = DownloadResult.CompletedSuccessfuly;
void OnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
if (otherObject.ShouldCancel)
{
((WebClient)sender).CancelAsync();
}
}
void OnDownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
if (e.Cancelled)
{
downloadResult = DownloadResult.Cancelled;
return;
}
if (e.Error != null)
{
downloadResult = DownloadResult.ErrorOccurred;
return;
}
}
try
{
Task task = wc.DownloadFileTaskAsync(url, fileName);
task.Wait();
}
catch (AggregateException ex)
{
}
switch (downloadResult)
{
case DownloadResult.CompletedSuccessfuly:
break;
case DownloadResult.Cancelled:
break;
case DownloadResult.ErrorOccurred:
break;
}
}
// Other work after downloading, regardless of cancellation.
// Could include in OnDownloadCompleted as long as this
// method blocked until all work was complete
return fileName;
}
}
public enum DownloadResult
{
CompletedSuccessfuly,
Cancelled,
ErrorOccurred
}
Run Code Online (Sandbox Code Playgroud)