服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - 使用 C# 下载文件的多种方法小结

使用 C# 下载文件的多种方法小结

2022-11-30 13:22Soar、毅 C#

本文从最简单的下载方式开始步步递进,讲述了文件下载过程中的常见问题并给出了解决方案。并展示了如何使用多线程提升 HTTP 的下载速度以及调用 aria2 实现非 HTTP 协议的文件下载,对C# 下载文件相关知识感兴趣的朋友一起看看

文件下载是一个软件开发中的常见需求。本文从最简单的下载方式开始步步递进,讲述了文件下载过程中的常见问题并给出了解决方案。并展示了如何使用多线程提升 HTTP 的下载速度以及调用 aria2 实现非 HTTP 协议的文件下载。

简单下载

在 .NET 程序中下载文件最简单的方式就是使用 WebClient 的 DownloadFile 方法:

?
1
2
3
4
5
6
var url = "https://www.coderbusy.com";
   var save = @"D:\1.html";
   using (var web = new WebClient())
   {
       web.DownloadFile(url,save);
   }

异步下载

该方法也提供异步的实现:

?
1
2
3
4
5
6
var url = "https://www.coderbusy.com";
   var save = @"D:\1.html";
   using (var web = new WebClient())
   {
       await web.DownloadFileTaskAsync(url, save);
   }

下载文件的同时向服务器发送自定义请求头

如果需要对文件下载请求进行定制,可以使用 HttpClient :

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var url = "https://www.coderbusy.com";
    var save = @"D:\1.html";
    var http = new HttpClient();
    var request = new HttpRequestMessage(HttpMethod.Get,url);
    //增加 Auth 请求头
    request.Headers.Add("Auth","123456");
    var response = await http.SendAsync(request);
    response.EnsureSuccessStatusCode();
    using (var fs = File.Open(save, FileMode.Create))
    {
        using (var ms = response.Content.ReadAsStream())
        {
            await ms.CopyToAsync(fs);
        }
    }

如何解决下载文件不完整的问题

以上所有代码在应对小文件的下载时没有特别大的问题,在网络情况不佳或文件较大时容易引入错误。以下代码在开发中很常见:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
var url = "https://www.coderbusy.com";
  var save = @"D:\1.html";
  if (!File.Exists(save))
  {
      Console.WriteLine("文件不存在,开始下载...");
      using (var web = new WebClient())
      {
          await web.DownloadFileTaskAsync(url, save);
      }
      Console.WriteLine("文件下载成功");
  }
  Console.WriteLine("开始处理文件");
  //TODO:对文件进行处理

如果在 DownloadFileTaskAsync 方法中发生了异常(通常是网络中断或网络超时),那么下载不完整的文件将会保留在本地系统中。在该任务重试执行时,因为文件已存在(虽然它不完整)所以会直接进入处理程序,从而引入异常。

一个简单的修复方式是引入异常处理,但这种方式对应用程序意外终止造成的文件不完整无效:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var url = "https://www.coderbusy.com";
   var save = @"D:\1.html";
   if (!File.Exists(save))
   {
       Console.WriteLine("文件不存在,开始下载...");
       using (var web = new WebClient())
       {
           try
           {
               await web.DownloadFileTaskAsync(url, save);
           }
           catch
           {
               if (File.Exists(save))
               {
                   File.Delete(save);
               }
               throw;
           }
       }
       Console.WriteLine("文件下载成功");
   }
   Console.WriteLine("开始处理文件");
   //TODO:对文件进行处理

笔者更喜欢的方式是引入一个临时文件。下载操作将数据下载到临时文件中,当确定下载操作执行完毕时将临时文件改名:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var url = "https://www.coderbusy.com";
    var save = @"D:\1.html";
    if (!File.Exists(save))
    {
        Console.WriteLine("文件不存在,开始下载...");
        //先下载到临时文件
        var tmp = save + ".tmp";
        using (var web = new WebClient())
        {
            await web.DownloadFileTaskAsync(url, tmp);
        }
        File.Move(tmp, save, true);
        Console.WriteLine("文件下载成功");
    }
    Console.WriteLine("开始处理文件");
    //TODO:对文件进行处理

使用 Downloader 进行 HTTP 多线程下载

在网络带宽充足的情况下,单线程下载的效率并不理想。我们需要多线程和断点续传才可以拿到更好的下载速度。

Downloader 是一个现代化的、流畅的、异步的、可测试的和可移植的 .NET 库。这是一个包含异步进度事件的多线程下载程序。Downloader 与 .NET Standard 2.0 及以上版本兼容,可以在 Windows、Linux 和 macOS 上运行。

使用 C# 下载文件的多种方法小结

GitHub 开源地址: https://github.com/bezzad/Downloader

NuGet 地址:https://www.nuget.org/packages/Downloader

从 NuGet 安装 Downloader 之后,创建一个下载配置:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var downloadOpt = new DownloadConfiguration()
   {
       BufferBlockSize = 10240, // 通常,主机最大支持8000字节,默认值为8000。
       ChunkCount = 8, // 要下载的文件分片数量,默认值为1
       MaximumBytesPerSecond = 1024 * 1024, // 下载速度限制为1MB/s,默认值为零或无限制
       MaxTryAgainOnFailover = int.MaxValue, // 失败的最大次数
       OnTheFlyDownload = false, // 是否在内存中进行缓存? 默认值是true
       ParallelDownload = true, // 下载文件是否为并行的。默认值为false
       TempDirectory = "C:\\temp", // 设置用于缓冲大块文件的临时路径,默认路径为Path.GetTempPath()。
       Timeout = 1000, // 每个 stream reader  的超时(毫秒),默认值是1000
       RequestConfiguration = // 定制请求头文件
       {
           Accept = "*/*",
           AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
           CookieContainer =  new CookieContainer(), // Add your cookies
           Headers = new WebHeaderCollection(), // Add your custom headers
           KeepAlive = false,
           ProtocolVersion = HttpVersion.Version11, // Default value is HTTP 1.1
           UseDefaultCredentials = false,
           UserAgent = $"DownloaderSample/{Assembly.GetExecutingAssembly().GetName().Version.ToString(3)}"
       }
   };

创建一个下载服务:

?
1
var downloader = new DownloadService(downloadOpt);

配置事件处理器(该步骤可以省略):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Provide `FileName` and `TotalBytesToReceive` at the start of each downloads
    // 在每次下载开始时提供 "文件名 "和 "要接收的总字节数"。
    downloader.DownloadStarted += OnDownloadStarted;
 
    // Provide any information about chunker downloads, like progress percentage per chunk, speed, total received bytes and received bytes array to live streaming.
    // 提供有关分块下载的信息,如每个分块的进度百分比、速度、收到的总字节数和收到的字节数组,以实现实时流。
    downloader.ChunkDownloadProgressChanged += OnChunkDownloadProgressChanged;
 
    // Provide any information about download progress, like progress percentage of sum of chunks, total speed, average speed, total received bytes and received bytes array to live streaming.
    // 提供任何关于下载进度的信息,如进度百分比的块数总和、总速度、平均速度、总接收字节数和接收字节数组的实时流。
    downloader.DownloadProgressChanged += OnDownloadProgressChanged;
 
    // Download completed event that can include occurred errors or cancelled or download completed successfully.
    // 下载完成的事件,可以包括发生错误或被取消或下载成功。
    downloader.DownloadFileCompleted += OnDownloadFileCompleted;

接着就可以下载文件了:

?
1
2
3
string file = @"D:\1.html";
    string url = @"https://www.coderbusy.com";
    await downloader.DownloadFileTaskAsync(url, file);

下载非 HTTP 协议的文件

除了 WebClient 可以下载 FTP 协议的文件之外,上文所示的其他方法只能下载 HTTP 协议的文件。

aria2 是一个轻量级的多协议和多源命令行下载工具。它支持 HTTP/HTTPS、FTP、SFTP、BitTorrent 和 Metalink。aria2 可以通过内置的 JSON-RPC 和 XML-RPC 接口进行操作。

我们可以调用 aria2 实现文件下载功能。

GitHub 地址:https://github.com/aria2/aria2

下载地址:https://github.com/aria2/aria2/releases

将下载好的 aria2c.exe 复制到应用程序目录,如果是其他系统则可以下载对应的二进制文件。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public static async Task Download(string url, string fn)
   {
       var exe = "aria2c";
       var dir = Path.GetDirectoryName(fn);
       var name = Path.GetFileName(fn);
 
       void Output(object sender, DataReceivedEventArgs args)
       {
           if (string.IsNullOrWhiteSpace(args.Data))
           {
               return;
           }
           Console.WriteLine("Aria:{0}", args.Data?.Trim());
       }
 
       var args = $"-x 8 -s 8 --dir={dir} --out={name} {url}";
       var info = new ProcessStartInfo(exe, args)
       {
           UseShellExecute = false,
           CreateNoWindow = true,
           RedirectStandardOutput = true,
           RedirectStandardError = true,
       };
       if (File.Exists(fn))
       {
           File.Delete(fn);
       }
 
       Console.WriteLine("启动 aria2c: {0}", args);
       using (var p = new Process { StartInfo = info, EnableRaisingEvents = true })
       {
           if (!p.Start())
           {
               throw new Exception("aria 启动失败");
           }
           p.ErrorDataReceived += Output;
           p.OutputDataReceived += Output;
           p.BeginOutputReadLine();
           p.BeginErrorReadLine();
           await p.WaitForExitAsync();
           p.OutputDataReceived -= Output;
           p.ErrorDataReceived -= Output;
       }
 
       var fi = new FileInfo(fn);
       if (!fi.Exists || fi.Length == 0)
       {
           throw new FileNotFoundException("文件下载失败", fn);
       }
   }

以上代码通过命令行参数启动了一个新的 aria2c 下载进程,并对下载进度信息输出在了控制台。调用方式如下:

?
1
2
3
var url = "https://www.coderbusy.com";
   var save = @"D:\1.html";
   await Download(url, save);

到此这篇关于使用 C# 下载文件的十八般武艺的文章就介绍到这了,更多相关C# 下载文件内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://www.cnblogs.com/Soar1991/p/15165595.html

延伸 · 阅读

精彩推荐
  • C#快速了解c# 常量

    快速了解c# 常量

    这篇文章主要介绍了c# 常量的相关资料,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下...

    菜鸟教程8632022-09-27
  • C#c# 用ICSharpCode组件压缩文件

    c# 用ICSharpCode组件压缩文件

    今天,主要总结下,windows平台下,zip的压缩与解压的方法,用ICSharpCode组件,有此需求的朋友可以参考下本文...

    micDavid4052022-11-23
  • C#C#实现关闭子窗口而不释放子窗口对象的方法

    C#实现关闭子窗口而不释放子窗口对象的方法

    下面小编就为大家带来一篇C#实现关闭子窗口而不释放子窗口对象的方法 。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来...

    C#教程网12162021-12-20
  • C#如何在c#中使用opencv函数库

    如何在c#中使用opencv函数库

    这篇文章主要介绍了如何在c#中使用opencv,对图像匹配处理感兴趣的同学可以参考下...

    firebolt20028612022-11-10
  • C#Json操作库DynamicJson使用指南

    Json操作库DynamicJson使用指南

    本文给大家分享的是专门为.NET程序员开发的Json操作库DynamicJson,其源码非常简单,仅仅只有400行代码,一个对应的class类,目前只支持.NET 4.0以上的.NET Fr...

    百小僧5542021-12-06
  • C#Winform学生信息管理系统登陆窗体设计(1)

    Winform学生信息管理系统登陆窗体设计(1)

    这篇文章主要为大家详细介绍了Winform学生信息管理系统登陆窗体设计思路,感兴趣的小伙伴们可以参考一下...

    丿木呈广予口贝8432021-11-23
  • C#深入理解C# 装箱和拆箱(整理篇)

    深入理解C# 装箱和拆箱(整理篇)

    通过装箱和拆箱操作,能够在值类型和引用类型中架起一做桥梁.换言之,可以轻松的实现值类型与引用类型的互相转换...

    幸福安康3872022-01-19
  • C#unity绘制一条流动的弧线(贝塞尔线)

    unity绘制一条流动的弧线(贝塞尔线)

    这篇文章主要为大家详细介绍了unity绘制一条流动弧线的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    BBoy_Leon5242022-09-15