如何处理使用WebClient.DownloadFileAsync时的异常

本文关键字:DownloadFileAsync WebClient 异常 何处理 处理 | 更新日期: 2023-09-27 17:51:16

我正在使用WebClient以以下方式从互联网下载一些文件:

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方法传递到我的主要捕获。正确的做法是什么?

如何处理使用WebClient.DownloadFileAsync时的异常

解决方案不理想

可以将异常保存在lambda之外定义的某个变量中。然后可以重新抛出:

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

为什么不好?因为您将丢失堆栈跟踪(它将显示异常是在当前方法中抛出的,而不是在WebClient中抛出的)。尽管如此,如果你不需要或者不关心stacktrace,这是一个可能的解决方案。

就地处理异常

你也可以创建一些方法来处理外部try-catch和下载的处理程序中的异常:

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<byte[]> 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。所以你也可以考虑用同样的方法使用任务延续:

public Task LoadFile()
{
    Task<Byte[]> 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);
}

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

你能做的是创建一个任务(一个异步操作),并使用ContinueWith指令来处理异常。这可能有点不可读

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使用基于事件的异步模式,你不能捕获异常,你可以得到异常抛出AsyncCompletedEventArgs。错误属性

相关文章:
  • 没有找到相关文章