适用于 .NET 的易于使用的 URL 文件下载器类






4.85/5 (23投票s)
一个易于使用的可续传文件下载器类,为 .NET 2.0 或 .NET 1.1 提供进度反馈
引言
在我公司的一个项目中,我需要能够从给定的 URL 下载文件。不幸的是,`WebClient` 类提供的方法除了下载数据之外,没有其他功能。我需要一些能够提供进度反馈并且可以续传的功能。
当我开始在网上搜索任何现有代码时,我偶然发现了 CodeProject 上的两篇文章,它们帮助我非常接近了我所需要的东西。第一篇 文章 由 John Batte 撰写,第二篇 文章 由 Phil Crosby 撰写,是他作品的衍生。以这两篇文章为起点,我得以进行了另一个衍生,完成了我的目标。
本文的代码示例和内容集中在 .NET 2.0 的实现上,任何代码更新都只针对该版本进行。提供 .NET 1.1 的实现仅是为了向后兼容,我没有进一步的计划对其进行修改。
Using the Code
`FileDownloader` 类是主要类,并且非常易于使用。最小实现如下:
private void DoDownload()
{
FileDownloader downloader = new FileDownloader();
downloader.Download(new Uri("https://codeproject.org.cn" +
"/info/stuff/cp_bg_black1024.gif"), @"C:\Temp");
}
这会创建一个 `FileDownloader` 类的新实例,并将位于 "https://codeproject.org.cn/info/stuff/cp_bg_black1024.gif" 的文件下载到 "C:\Temp" 文件夹。
`FileDownloader` 实际上异步执行所有下载操作。当调用 `FileDownloader.Download(Uri url, string destinationFolder)` 方法时,它会通过等待一个信号设置指示下载已完成来完成同步下载。这样就可以通过 `Download` 方法进行同步下载,或者通过 `DownloadAsync` 方法进行异步下载。同步和异步方法最终都调用相同的内部方法来执行实际下载。
这个内部方法名为 `DownloadAsyncCore`,它实例化了一个 `DownloadInfo` 内部类。这个类在性质上类似于 .NET CLR 提供的 `FileInfo` 类,并为 `FileDownloader` 提供了下载文件所需的所有信息。这包括:
- 正在下载的文件长度(如果已知)
- 用于实际下载文件的 `Stream`
- 正在服务请求的 `WebResponse`
一旦 `DownloadInfo` 类被实例化,数据就会以 1KB 的块下载并写入磁盘。这是默认的块大小,可以通过 `BlockSize` 属性进行更改。正是这种实现允许在下载被取消或中断时恢复下载。
虽然此实现是功能性的,但它阻止了任何通过代理或需要身份验证才能访问所请求 URL 的用户使用该类。为了支持这些场景,`FileDownloader` 类同时提供了 `Proxy` 属性(接受 `IWebProxy` 派生对象)和 `Credentials` 属性(接受 `ICredentials` 派生对象)。这些属性也会传递给底层的 `DownloadInfo` 对象。
最后,事件提供了进度反馈。 .NET 2.0 中的 `WebClient` 类具有提供此类事件的异步下载方法。这些方法由 `DownloadCompletedEventArgs` 和 `DownloadProgressChangedEventArgs` `EventArgs` 派生类提供服务。不幸的是,这两个类都有内部构造函数,并且是 .NET 2.0 中的新功能,因此我无法使用它们。取而代之的是,我创建了一个新的 `EventArgs` 派生类三元组来处理 `FileDownloader` 类公开的三个事件。这些事件是:
public event EventHandler<FileDownloadCompletedEventArgs> DownloadCompleted
public event EventHandler<FileDownloadProgressChangedEventArgs> DownloadProgressChanged;
public event EventHandler<FileDownloadStatusChangedEventArgs> DownloadStatusChanged;
这些事件在实际文件下载过程中被触发,以便向调用者提供状态信息(`DownloadStatusChanged`)、进度信息(`DownloadProgressChanged`)和完成信息(`DownloadCompleted`)。一个完整的示例看起来是这样的:
private void DoDownload()
{
Uri fileUrl = new Uri("https://codeproject.org.cn" +
"/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<FileDownloadCompletedEventArgs>(OnDownloadCompleted);
downloader.DownloadProgressChanged += new
EventHandler<FileDownloadProgressChangedEventArgs>(
OnDownloadProgressChanged);
downloader.DownloadStatusChanged += new
EventHandler<FileDownloadStatusChangedEventArgs>(
OnDownloadStatusChanged);
// Download the requested file to the specified folder
downloader.Download(fileUrl, tempPath);
}
private void OnDownloadCompleted(object sender,
FileDownloadCompletedEventArgs e)
{
// Do something when the download completes.
}
private void OnDownloadProgressChanged(object sender,
FileDownloadProgressChangedEventArgs e)
{
// Do something when the progress changes,
// usually you would increment a progress bar.
}
private void OnDownloadStatusChanged(object sender,
FileDownloadStatusChangedEventArgs e)
{
// Do something when that status changes.
}
以下是使用 VB.NET 的相同示例。该示例基于 评论 [^] 由 CBlackburn [^] 提供。
Private Sub DoDownload()
Dim fileUrl As Uri = New Uri("https://codeproject.org.cn/" & _
"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
Private Sub OnDownloadCompleted(ByVal sender As Object, _
ByVal e As FileDownloadCompletedEventArgs)
' Do something when the download completes.
End Sub
Private Sub OnDownloadProgressChanged(ByVal sender As Object, _
ByVal e As FileDownloadProgressChangedEventArgs)
' Do something when the progress changes,
' usually you would increment a progress bar.
End Sub
Private Sub OnDownloadStatusChanged(ByVal sender As Object, _
ByVal e As FileDownloadStatusChangedEventArgs)
' Do something when that status changes.
End Sub
`FileDownloader` 类的完整公共 API 在下载包附带的文档 CHM 文件中提供。
关注点
虽然 `FileDownloader` 类同时提供 .NET 2.0 和 .NET 1.1 解决方案,但只有 .NET 2.0 解决方案已更新,以提供异步下载功能以及修订历史中列出的其他修改。
`DownloadInfo` 类实现了 `IDisposable` 模式。但是,此模式的实现方式可能与许多人之前看到的有所不同。有关更多信息,请参阅本文:正确实现 IDisposable 和 Dispose 模式 [^] 。
修订历史
2007年8月16日
- 添加了设置 User-agent HTTP 头的功能。
2007年8月12日
- 添加了异步支持
- 添加了检索文件最后修改日期的功能
- 添加了对 Web 请求进行预认证的支持
- 添加了下载 HTML 页面的功能
- 添加了在文件已存在时覆盖下载文件的功能
2007年1月11日
- 添加了一个完整的 VB.NET 示例
2006年8月27日
- 更新了源代码,以包含应用程序配置文件
2006年8月25日
- 原文