使用WebClient.DownloadFileAsync时如何处理exception

我通过以下方式使用WebClient从Internet下载一些文件:

 try { ManualResetEvent mr = new ManualResetEvent(false); mr.Reset(); using (WebClient wc = new WebClient()) { wc.DownloadFileCompleted += ((sender, args) => { if (args.Error == null) { File.Move(filePath, Path.ChangeExtension(filePath, ".jpg")); mr.Set(); } else { //how to pass args.Error? } }); wc.DownloadFileAsync(new Uri(string.Format("{0}/{1}", Settings1.Default.WebPhotosLocation, Path.GetFileName(f.FullName))), filePath); mr.WaitOne(); } } catch (Exception ex) { //Catch my error here and handle it (display message box) } 

但我似乎无法将我的匿名DownloadFileCompleted方法的错误传递给我的主要问题。 这样做的正确方法是什么?

重新抛出的糟糕解决方案

您可以将exception保存在lambda外部定义的某个变量中。 然后它可以被重新抛出:

 Exception exc = null; using (WebClient wc = new WebClient()) { wc.DownloadFileCompleted += ((sender, args) => ... mr.WaitOne(); if (exception != null) throw exception; } 

为什么不好? 因为你将松散堆栈跟踪(它将显示在当前方法中抛出exception,而不是在WebClient中)。 不过,如果你不需要或不关心堆栈跟踪,那么这是可能的解决方案。

处理exception到位

您还可以创建一些方法来处理外部try-catch和下载的处理程序中的exception:

 void HandleWebClientException(Exception exc) { ... } try { ManualResetEvent mr = new ManualResetEvent(false); mr.Reset(); using (WebClient wc = new WebClient()) { wc.DownloadFileCompleted += ((sender, args) => { if (args.Error == null) { File.Move(filePath, Path.ChangeExtension(filePath, ".jpg")); mr.Set(); } else { HandleWebClientException(args.Error); } }); wc.DownloadFileAsync(new Uri(string.Format("{0}/{1}", Settings1.Default.WebPhotosLocation, Path.GetFileName(f.FullName))), filePath); mr.WaitOne(); } } catch (Exception ex) { HandleWebClientException(ex); } 

做得对

最好的想法是避免在WebClient上使用void方法,因为您无法等待它们或应用某些延续 。

这些方法在某种意义上很方便,但它们会强制您使用具有同步构造的秘密解决方案,以使工作流程更少依赖于不同的回调。

要使用async-await,您必须应用public Task DownloadDataTaskAsync(Uri address)方法。

你可以:

1. await它获取数据的字节数组以便以后手动保存它,但它需要在应用程序中进行可靠的返工以使其始终保持异步 :

 public async Task LoadFile() { try { using (WebClient wc = new WebClient()) { var bytes = await wc.DownloadDataTaskAsync(new Uri(string.Format("{0}/{1}", Settings1.Default.WebPhotosLocation, Path.GetFileName(f.FullName))), filePath); System.IO.File.WriteAllBytes(bytes); // Probably turn it into async too } } catch (Exception ex) { //Catch my error here and handle it (display message box) } } 

它会工作,但我不确定DownloadDataTaskAsync是一个真正的异步方法。

2.因此,您也可以考虑使用相同的方法使用Task Continuations :

 public Task LoadFile() { Task bytesTask = wc.DownloadDataTaskAsync(new Uri(string.Format("{0}/{1}", Settings1.Default.WebPhotosLocation, Path.GetFileName(f.FullName))), filePath); var success = bytesTask.ContinueWith((prev) => { System.IO.File.WriteAllBytes(prev.Result); }, TaskContinuationOptions.OnlyOnRanToCompletion); var failure = bytesTask.ContinueWith(prev => { MessageBox.Show //... }, TaskContinuationOptions.OnlyOnFaulted); return Task.WhenAny(success, failure); } 

PS:如果你不需要异步加载文件,为什么不使用简单的阻塞方法public void DownloadFile(Uri address, string fileName)

您可以做的是创建一个Task(异步操作)并使用ContinueWith指令来处理exception。 这可能有点难以理解

 using (WebClient wc = new WebClient()) { wc.DownloadFileCompleted += ((sender, args) => { if (args.Error == null) { File.Move(filePath, Path.ChangeExtension(filePath, ".jpg")); mr.Set(); } else { //how to pass args.Error? } }); Task.Factory.StartNew(() => wc.DownloadFile( new Uri(string.Format("{0}/{1}", Settings1.Default.WebPhotosLocation, Path.GetFileName(f.FullName))), filePath)) .ContinueWith(t => Console.WriteLine(t.Exception.Message)); } 

但是,随着.NET 4.5的推出,Webclient会为您公开基于异步下载的任务!

 using (WebClient wc = new WebClient()) { wc.DownloadFileCompleted += ((sender, args) => { if (args.Error == null) { File.Move(filePath, Path.ChangeExtension(filePath, ".jpg")); mr.Set(); } else { //how to pass args.Error? } }); wc.DownloadFileTaskAsync(new Uri(string.Format("{0}/{1}", Settings1.Default.WebPhotosLocation, Path.GetFileName(f.FullName))), filePath) .ContinueWith(t => t.Exception.Message) } 

您应该使用awaitDownloadFileTaskAsync

 try { using (WebClient wc = new WebClient()) { await wc.DownloadFileTaskAsync(new Uri(string.Format("{0}/{1}", Settings1.Default.WebPhotosLocation, Path.GetFileName(f.FullName))), filePath); } } catch (Exception ex) { //Catch my error here and handle it (display message box) } 

DownloadFileAsync使用基于事件的异步模式 ,你无法捕获exception,你可以获得exception抛出AsyncCompletedEventArgs.Error属性