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

C# 使用WebClient.DownloadFileAsync时如何处理异常,c#,error-handling,webclient,C#,Error Handling,Webclient,我正在使用WebClient以以下方式从internet下载一些文件: try { ManualResetEvent mr = new ManualResetEvent(false); mr.Reset(); using (WebClient wc = new WebClient()) { wc.DownloadFileCompleted += ((sender, args) => { if

我正在使用
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)
}

但是我似乎无法将错误从我的annonymous
DownloadFileCompleted
方法传递到我的主要捕获。正确的方法是什么?

您应该使用
wait
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使用,您无法捕获异常,您可以获取异常抛出属性

您可以做的是创建一个任务(异步操作)并使用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)
}
带回缩的不良溶液 可以将异常保存在lambda外部定义的某个变量中。然后可以将其重新收回:

Exception exc = null;
using (WebClient wc = new WebClient())
{
      wc.DownloadFileCompleted += ((sender, args) =>
      ...

      mr.WaitOne();

      if (exception != null) throw exception;
}
为什么不好?因为您将丢失stacktrace(它将显示异常是在当前方法中引发的,而不是在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);
}
做对了 最好的办法是避免在上使用void方法,因为您不能在它们上使用void方法或应用某些方法

这些方法在某种意义上是方便的,但它们迫使您使用带有同步结构的秘密解决方案,以减少工作流对不同回调的依赖

要使用AsyncWait,您必须应用方法

您可以:

1。
等待
获取数据的字节数组,以便以后手动保存,但这需要在应用程序中进行彻底的返工,以使其:

它会起作用,但我不确定
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);
}
公共任务加载文件()
{
Task bytesTask=wc.DownloadDataTaskAsync(新Uri(string.Format(“{0}/{1}”),Settings1.Default.WebPhotosLocation,Path.GetFileName(f.FullName)),filePath);
var success=bytesTask.ContinueWith((上一个)=>
{
System.IO.File.writealBytes(上一个结果);
},
TaskContinuationOptions.OnlyOnRanToCompletion);
var failure=bytesTask.ContinueWith(上一个=>
{
MessageBox.Show/。。。
},
TaskContinuationOptions.OnlyOnFaulted);
返回任务。何时(成功、失败);
}

p.S.:如果不需要异步加载文件,为什么不使用简单的阻塞方法呢?

回答得很好!我使用的是.NET4,所以不能使用
wait
关键字。我可能会选择“糟糕的解决方案”,因为我不需要
StackTrace
。我不使用阻塞方法的原因也是因为我需要用进度更新UI(我在示例中没有提到)@user1您可以在.Net 4中使用async/wait。只需在Nuget中运行
安装包Microsoft.Bcl.Async
命令。有关更多详细信息,请参见此
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);
}