在工作项目中,需要从给定的URL下载文件。遗憾的是,WebClient类提供的方法除了下载数据外,没有提供任何额外的功能。需要的不仅仅是下载,还需要能够提供进度反馈,并且支持断点续传。
在Web上搜索现有代码时,找到了CodeProject上的两篇文章,它们帮助接近所需要的功能。第一篇文章是John Batte写的,第二篇是Phil Crosby基于John Batte的工作衍生出来的。利用这两篇文章作为起点,进行了进一步的衍生,实现了目标。
本文的代码示例和内容专注于.NET 2.0实现,任何代码更新都只针对该版本。.NET 1.1实现仅用于向后兼容,没有计划继续对其进行更改。
FileDownloader类是主要的类,使用起来非常简单。最小实现如下:
private void DoDownload()
{
FileDownloader downloader = new FileDownloader();
downloader.Download(new Uri("http://www.codeproject.com/info/stuff/cp_bg_black1024.gif"), @"C:\Temp");
}
这将创建FileDownloader类的一个新实例,并下载位于"http://www.codeproject.com/info/stuff/cp_bg_black1024.gif"的文件到"C:\Temp"文件夹。
FileDownloader实际上执行所有下载操作都是异步的。当调用FileDownloader.Download(Uri url, string destinationFolder)方法时,它通过等待一个信号来完成同步下载,该信号指示下载已完成。这允许通过使用Download方法进行同步下载,或者通过使用DownloadAsync方法进行异步下载。同步和异步方法最终都调用同一个内部方法来执行实际的下载。
这个内部方法叫做DownloadAsyncCore,它实例化了一个DownloadInfo内部类。这个类与.NETCLR提供的FileInfo类性质相似,为FileDownloader提供了实际下载文件所需的所有信息。包括:
一旦DownloadInfo类被实例化,数据就会以1KB的块大小下载并写入磁盘。这是默认的块大小,可以通过BlockSize属性进行更改。正是这种实现方式允许下载在被取消或中断时可以恢复。
虽然这种实现是功能性的,但它确实阻止了任何在代理后面或需要身份验证才能访问请求的URL的人使用该类。为了支持这些场景,FileDownloader类提供了一个Proxy属性,该属性接受一个派生自IWebProxy的对象,以及一个Credentials属性,该属性接受一个派生自ICredentials的对象。这些属性也被传递给底层的DownloadInfo对象。
最后,事件提供了进度反馈。.NET2.0中的WebClient类具有异步下载方法,提供了这样的事件。这些方法由DownloadCompletedEventArgs和DownloadProgressChangedEventArgs EventArgs派生类服务。不幸的是,这两个类都有内部构造函数,并且是.NET 2.0中的新内容,所以无法使用它们。相反,创建了一个新的EventArgs派生类三元组来处理FileDownloader类公开的三个事件。这些事件是:
public event EventHandler DownloadCompleted;
public event EventHandler DownloadProgressChanged;
public event EventHandler DownloadStatusChanged;
这些事件在实际文件下载过程中被引发,以便为调用者提供状态信息(DownloadStatusChanged)、进度信息(DownloadProgressChanged)和完成信息(DownloadCompleted)。
以下是一个完整的示例:
private void DoDownload()
{
Uri fileUrl = new Uri("http://www.codeproject.com/info/stuff/cp_bg_black1024.gif");
string tempPath = Path.GetTempPath();
string fileName = Path.GetFileName(fileUrl.AbsoluteUri);
string downloadPath = Path.Combine(tempPath, fileName);
FileDownloader downloader = new FileDownloader();
downloader.Proxy = WebRequest.DefaultWebProxy;
downloader.Credentials = CredentialCache.DefaultCredentials;
downloader.DownloadCompleted += new EventHandler(OnDownloadCompleted);
downloader.DownloadProgressChanged += new EventHandler(OnDownloadProgressChanged);
downloader.DownloadStatusChanged += new EventHandler(OnDownloadStatusChanged);
downloader.Download(fileUrl, tempPath);
}
以下是使用VB.NET的相同示例。这个示例基于CBlackburn的注释。
Private Sub DoDownload()
Dim fileUrl As Uri = New Uri("http://www.codeproject.com/info/stuff/cp_bg_black1024.gif")
Dim tempPath As String = Path.GetTempPath()
Dim fileName As String = Path.GetFileName(fileUrl.AbsoluteUri)
Dim downloadPath As String = Path.Combine(tempPath, fileName)
Dim downloader As FileDownloader = New FileDownloader()
downloader.Proxy = WebRequest.DefaultWebProxy
downloader.Credentials = CredentialCache.DefaultCredentials
AddHandler downloader.DownloadCompleted, AddressOf OnDownloadCompleted
AddHandler downloader.DownloadProgressChanged, AddressOf OnDownloadProgressChanged
AddHandler downloader.DownloadStatusChanged, AddressOf OnDownloadStatusChanged
downloader.Download(fileUrl, tempPath)
End Sub