使用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
方法。
你可以:
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) }
您应该使用await
和DownloadFileTaskAsync
:
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属性