65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (23投票s)

2006 年 8 月 25 日

CPOL

4分钟阅读

viewsIcon

240419

downloadIcon

3013

一个易于使用的可续传文件下载器类,为 .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日

  • 原文
© . All rights reserved.