2.4下载网络资源

HTTP 远程请求最常见的应用场景之一是下载网络资源并将其保存到本地磁盘,这包括下载网页内容、图片、压缩包以及安装软件等。以下示例展示了如何下载 ASP.NET Core 运行时:

HTTP 远程请求最常见的应用场景之一是下载网络资源并将其保存到本地磁盘,这包括下载网页内容、图片、压缩包以及安装软件等。以下示例展示了如何下载 ASP.NET Core 运行时:

cs
// 从指定 URL 下载 ASP.NET Core 运行时,并保存到 C:\Workspaces\ 目录中// 如果未指定文件名,框架将自动从下载地址中解析出文件名,例如:aspnetcore-runtime-8.0.10-win-x64.exevar fileTransferResult = await httpRemoteService.DownloadFileAsync("https://download.visualstudio.microsoft.com/download/pr/a17b907f-8457-45a8-90db-53f2665ee49e/49bccd33593ebceb2847674fe5fd768e/aspnetcore-runtime-8.0.10-win-x64.exe"    , @"C:\Workspaces\");   // 如需指定文件名可设置为 C:\Workspaces\aspnetcore-runtime.exe

文件下载完成后,框架将返回一个 FileTransferResult 对象,包含以下属性:

  • IsSuccess:传输是否成功完成(bool 类型)。注意:因文件存在而跳过也被视为成功。
  • RequestUri:文件传输地址(string 类型)。文件下载时,为下载地址;文件上传时,为上传地址。
  • FilePath:文件的路径(string 类型)。
  • FileSize:文件的大小(以字节为单位的 long 类型)。
  • ElapsedMilliseconds:传输耗时(以毫秒为单位的 long 类型)。
  • StatusCode:响应状态(HttpStatusCode 类型)。

若本地文件已存在,将会抛出 InvalidOperationException 异常,System.InvalidOperationException: The destination path 'C:\Workspaces\aspnetcore-runtime-8.0.10-win-x64.exe' already exists.。此时,您可以通过 fileExistsBehavior 参数来指定文件存在时的行为:

cs
var fileTransferResult = await httpRemoteService.DownloadFileAsync("https://download.visualstudio.microsoft.com/download/pr/a17b907f-8457-45a8-90db-53f2665ee49e/49bccd33593ebceb2847674fe5fd768e/aspnetcore-runtime-8.0.10-win-x64.exe"    , @"C:\Workspaces\"    , fileExistsBehavior: FileExistsBehavior.Overwrite);    // 若文件存在时则覆盖

FileExistsBehavior 枚举包含以下选项:

  • CreateNew(默认值):若文件已存在,则抛出异常;否则,创建新文件。
  • Overwrite:覆盖现有文件。
  • Skip:保留现有文件,并跳过下载操作。

在下载文件时,您还可以获取实时的下载进度。以下示例展示了如何打印下载进度:

cs
 var fileTransferResult = await httpRemoteService.DownloadFileAsync("https://download.visualstudio.microsoft.com/download/pr/a17b907f-8457-45a8-90db-53f2665ee49e/49bccd33593ebceb2847674fe5fd768e/aspnetcore-runtime-8.0.10-win-x64.exe"     , @"C:\Workspaces\"     , async progress =>     {         Console.WriteLine(await progress.ToSummaryStringAsync()); // 输出简要进度字符串     }     , fileExistsBehavior: FileExistsBehavior.Overwrite);

下载进度的控制台输出示例(使用 progress.ToSummaryString()):

bash
Transferred 0.26MB of 10.09MB (2.63% complete, Speed: 3.86MB/s, Time: 0.07s, ETA: 2.55s). File: aspnetcore-runtime-8.0.10-win-x64.exe, Path: C:\Workspaces\aspnetcore-runtime-8.0.10-win-x64.exe.Transferred 10.09MB of 10.09MB (100.00% complete, Speed: 9.99MB/s, Time: 1.01s, ETA: 0.00s). Done! File: aspnetcore-runtime-8.0.10-win-x64.exe, Path: C:\Workspaces\aspnetcore-runtime-8.0.10-win-x64.exe.

若需在控制台中实时显示文件下载进度,推荐使用 UpdateConsoleProgressAsync() 方法。示例如下:

cs
 var fileTransferResult = await httpRemoteService.DownloadFileAsync("https://download.visualstudio.microsoft.com/download/pr/a17b907f-8457-45a8-90db-53f2665ee49e/49bccd33593ebceb2847674fe5fd768e/aspnetcore-runtime-8.0.10-win-x64.exe"     , @"C:\Workspaces\"     , progress => progress.UpdateConsoleProgressAsync() // 在控制台中更新文件传输进度条     , fileExistsBehavior: FileExistsBehavior.Overwrite);// ✅ 或使用 DownloadFileWithConsoleProgressAsync 方法(带控制台进度打印) var fileTransferResult = await httpRemoteService.DownloadFileWithConsoleProgressAsync("https://download.visualstudio.microsoft.com/download/pr/a17b907f-8457-45a8-90db-53f2665ee49e/49bccd33593ebceb2847674fe5fd768e/aspnetcore-runtime-8.0.10-win-x64.exe"     , @"C:\Workspaces\"     , fileExistsBehavior: FileExistsBehavior.Overwrite);

执行后,控制台将显示如下进度信息:

bash
File: aspnetcore-runtime-8.0.10-win-x64.exe, Path: C:\Workspaces\aspnetcore-runtime-8.0.10-win-x64.exe[##############################                    ] 61.35% (6.19MB/10.09MB) Speed: 5.81MB/s, Time: 1.07s, ETA: 0.67s.

若使用 progress.ToString(),则控制台输出将包含更详细的进度信息:

bash
Transfer Progress:        File Name:                        aspnetcore-runtime-8.0.10-win-x64.exe        File Path:                        C:\Workspaces\aspnetcore-runtime-8.0.10-win-x64.exe        File Size:                        10.09MB        Transferred:                      0.12MB        Percentage Complete:              1.23%        Transfer Rate:                    2.20MB/s        Time Elapsed (s):                 0.06        Estimated Time Remaining (s):     4.52Transfer Progress:        File Name:                        aspnetcore-runtime-8.0.10-win-x64.exe        File Path:                        C:\Workspaces\aspnetcore-runtime-8.0.10-win-x64.exe        File Size:                        10.09MB        Transferred:                      10.09MB        Percentage Complete:              100.00%        Transfer Rate:                    9.77MB/s        Time Elapsed (s):                 1.03        Estimated Time Remaining (s):     0.00

progress 参数的类型为 FileTransferProgress,包含以下属性和方法:

  • 属性

    • FilePath:文件的路径(string 类型)。
    • FileName:文件的名称(string 类型)。
    • FileSize:文件的大小(以字节为单位的 long 类型)。
    • Transferred:已传输的数据量(以字节为单位的 long 类型)。
    • PercentageComplete:已完成的传输百分比(double 类型)。
    • TransferRate:当前的传输速率(以字节/秒为单位的 double 类型)。
    • TimeElapsed:从开始传输到现在的持续时间(TimeSpan 类型)。
    • EstimatedTimeRemaining:预估的剩余传输时间(TimeSpan 类型)。
  • 方法

    • ToString():输出带缩进的详细进度字符串。
    • ToStringAsync():输出带缩进的详细进度字符串。
    • ToSummaryString():输出简要的进度字符串。
    • ToSummaryStringAsync():输出简要的进度字符串。
    • UpdateConsoleProgress():在控制台中更新(打印)文件传输进度条。
    • UpdateConsoleProgressAsync():在控制台中更新(打印)文件传输进度条。

并行下载多个文件#

框架原生支持并行下载多个文件。借助 ParallelUtility.ForEachAsync 工具方法,可以轻松实现并发下载,并自动启用多行进度条模式——每个文件独占两行(文件头和进度条),所有进度条同屏实时刷新,互不干扰:

cs
var urls = new[]{    "https://img-s.msn.cn/tenant/amp/entityid/AA296jTM.img?w=640&h=1068&m=6",    "https://img-s.msn.cn/tenant/amp/entityid/AA297bnQ.img?w=640&h=1240&m=6&x=236&y=233&s=64&d=64",    "https://img-s.msn.cn/tenant/amp/entityid/AA296Rr4.img?w=640&h=821&m=6"};const string saveDir = @"C:\Workspaces\";// 并行下载,默认最大并发数为 4await ParallelUtility.ForEachAsync(urls, async (url, token) =>    {        await _httpRemoteService.DownloadFileWithConsoleProgressAsync(url, saveDir, FileExistsBehavior.Overwrite, cancellationToken: token);    });

执行后,控制台将同时显示所有文件的下载进度,每个文件的进度条独立刷新:

bash
File: AA296jTM.img, Path: C:\Workspaces\AA296jTM.img[########............] 40.12% (0.05MB/0.12MB) Speed: 1.20MB/s, Time: 42ms, ETA: 58ms.File: AA297bnQ.img, Path: C:\Workspaces\AA297bnQ.img[######..............] 30.05% (0.04MB/0.12MB) Speed: 0.95MB/s, Time: 38ms, ETA: 84ms.File: AA296Rr4.img, Path: C:\Workspaces\AA296Rr4.img[##########..........] 50.33% (0.06MB/0.12MB) Speed: 1.55MB/s, Time: 45ms, ETA: 39ms.

所有文件下载完成后,进度条将逐一显示 Done! 状态:

bash
File: AA296jTM.img, Path: C:\Workspaces\AA296jTM.img[####################] 100.00% (0.12MB/0.12MB) Speed: 2.61MB/s, Time: 167ms. Done!File: AA297bnQ.img, Path: C:\Workspaces\AA297bnQ.img[####################] 100.00% (0.12MB/0.12MB) Speed: 1.81MB/s, Time: 203ms. Done!File: AA296Rr4.img, Path: C:\Workspaces\AA296Rr4.img[####################] 100.00% (0.12MB/0.12MB) Speed: 1.61MB/s, Time: 204ms. Done!

您还可以通过 maxDegreeOfParallelism 参数控制最大并发数:

cs
await ParallelUtility.ForEachAsync(urls, async (url, token) =>    {        await _httpRemoteService.DownloadFileWithConsoleProgressAsync(url, saveDir, FileExistsBehavior.Overwrite, cancellationToken: token);    },    maxDegreeOfParallelism: 2);   // 最多同时下载 2 个文件

除了上述方式,还支持以下几种方法下载网络资源:

cs
// 使用构建器模式var fileTransferResult = await httpRemoteService.SendAsync(HttpRequestBuilder.DownloadFile("https://download.visualstudio.microsoft.com/download/pr/a17b907f-8457-45a8-90db-53f2665ee49e/49bccd33593ebceb2847674fe5fd768e/aspnetcore-runtime-8.0.10-win-x64.exe"    , @"C:\Workspaces\"    , fileExistsBehavior: FileExistsBehavior.Overwrite));// 更多详细用法可参考第 19.2.1 节