使用 C# 下载文件的十八般武艺
文件下载是一个软件开发中的常见需求。本文从最简单的下载方式开始步步递进,讲述了文件下载过程中的常见问题并给出了解决方案。并展示了如何使用多线程提升 HTTP 的下载速度以及调用 aria2 实现非 HTTP 协议的文件下载。
简单下载
在 .NET 程序中下载文件最简单的方式就是使用 WebClient 的 DownloadFile 方法:
var url = "https://www.coderbusy.com";
var save = @"D:\1.html";
using (var web = new WebClient())
{
web.DownloadFile(url,save);
}
异步下载
该方法也提供异步的实现:
var url = "https://www.coderbusy.com";
var save = @"D:\1.html";
using (var web = new WebClient())
{
await web.DownloadFileTaskAsync(url, save);
}
下载文件的同时向服务器发送自定义请求头
如果需要对文件下载请求进行定制,可以使用 HttpClient :
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);
}
}
如何解决下载文件不完整的问题
以上所有代码在应对小文件的下载时没有特别大的问题,在网络情况不佳或文件较大时容易引入错误。以下代码在开发中很常见:
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 方法中发生了异常(通常是网络中断或网络超时),那么下载不完整的文件将会保留在本地系统中。在该任务重试执行时,因为文件已存在(虽然它不完整)所以会直接进入处理程序,从而引入异常。
一个简单的修复方式是引入异常处理,但这种方式对应用程序意外终止造成的文件不完整无效:
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:对文件进行处理
笔者更喜欢的方式是引入一个临时文件。下载操作将数据下载到临时文件中,当确定下载操作执行完毕时将临时文件改名:
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 上运行。

GitHub 开源地址: https://github.com/bezzad/Downloader
NuGet 地址:https://www.nuget.org/packages/Downloader
从 NuGet 安装 Downloader 之后,创建一个下载配置:
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)}"
}
};
创建一个下载服务:
var downloader = new DownloadService(downloadOpt);
配置事件处理器(该步骤可以省略):
// 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;
接着就可以下载文件了:
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 复制到应用程序目录,如果是其他系统则可以下载对应的二进制文件。
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 下载进程,并对下载进度信息输出在了控制台。调用方式如下:
var url = "https://www.coderbusy.com";
var save = @"D:\1.html";
await Download(url, save);
使用 C# 下载文件的十八般武艺的更多相关文章
- Java下载文件(流的形式)
@RequestMapping("download") @ResponseBody public void download(HttpServletResponse respons ...
- 使用批处理文件在FTP服务器 上传下载文件
1.从ftp服务器根目录文件夹下的文件到指定的文件夹下 格式:ftp -s:[配置文件] [ftp地址] 如:ftp -s:c:\vc\ftpconfig.txt 192.168.1.1 建立一个 ...
- 通过form表单的形式下载文件。
在项目中遇到问题,要求动态拼接uri下载文件.但是由于项目的安全拦截导致window.location.href 和 window.open等新建窗口的方法都不行. 无意间百度到了通过form表单来下 ...
- SecureCRT上传和下载文件
SecureCRT上传和下载文件(下载默认目录) SecureCR 下的文件传输协议有ASCII .Xmodem .Ymodem .Zmodem ASCII:这是最快的传输协议,但只能传送文本文件. ...
- HTTP 错误 404.3 – Not Found 由于扩展配置问题而无法提供您请求的页面。如果该页面是脚本,请添加处理程序。如果应下载文件,请添加 MIME 映射。
今天,在vs2013中新建了一个placard.json文件,当我用jq读取它的时候,去提示404,直接在浏览器访问这个文件,提示: HTTP 错误 404.3 – Not Found 由于扩展配置问 ...
- FTP下载文件失败
这几天的定时任务下载文件的脚本失败了. 于是手工执行测试,发现报550 Permission denied. Passive mode refused. 意思就是被动模式下,没有权限获取文件. 解决方 ...
- 如何使用FileZilla上传和下载文件
一.使用FileZilla上传文件 1 打开 FileZilla 按照如下图所示,填写远程 Linux 的 IP ,用户名,密码,还有端口号(默认22) 2 选中左边需要上传的文件,然后拖到右边,等待 ...
- 开发板tftp下载文件
搭建过程: 1.安装相关软件包:tftpd(服务端),tftp(客户端),xinetd sudo apt-get install tftpd tftp xinetd 2.建立配置文件(蓝色的目录是可以 ...
- Linux上传下载文件快捷命令
远程链接Linux(如SecrueCRT),要上传文件很下载文件到Linux服务器,只需要使用sz或者rz命令即可快速下载和上传文件了. 使用方法: 1.首先确保Linux服务器系统中安装了lrzsz ...
随机推荐
- 【重学Java】Stream流
Stream流 体验Stream流[理解] 案例需求 按照下面的要求完成集合的创建和遍历 创建一个集合,存储多个字符串元素 把集合中所有以"张"开头的元素存储到一个新的集合 把&q ...
- 最常见的安全漏洞– Acunetix Web应用程序漏洞报告2021
每年,Acunetix都会为您提供最常见的Web安全漏洞和网络外围漏洞的分析.我们的年度Web应用程序漏洞报告(现已成为Invicti AppSec指标的一部分)是基于从Acunetix在线获得的真实 ...
- ARTS第八周
1.Algorithm:每周至少做一个 leetcode 的算法题2.Review:阅读并点评至少一篇英文技术文章3.Tip:学习至少一个技术技巧4.Share:分享一篇有观点和思考的技术文章 以下是 ...
- excle名字后面直接跟别的出来
名字后面直接跟别的出来 =IF($E6="","",VLOOKUP(E6,通讯录!$B$2:$D$1000,3,0)) $E6:是填写位置的地方 VLOOKUP ...
- css 层叠上下文和层叠顺序
层叠上下文是css中的一个三维概念,拥有层叠上下文的元素在z轴上比普通元素要高,而且其内部的所有内容是自成体系的,其后代都是在它的层叠顺序中 哪些元素拥有层叠上下文 1.根元素,也就是html默认拥有 ...
- 配置Mac 终端高亮
mac下所有vim的配色方案的样式. 下面讲解如何设置这些好看的配色 首先:在终端输入 vim ~/.bash_profile 查看是否有上面提到的某些配色,所有配色均是以.vim结束的,果有的话,再 ...
- 小白都能理解的TCP三次握手四次挥手
前言 TCP在学习网络知识的时候是经常的被问到知识点,也是程序员必学的知识点,今天小杨用最直白的表述带大家来认识认识,喜欢的朋友记得点点关注哈. 何为TCP 上点官方的话:是一种面向连接(连接导向)的 ...
- CentOS 命令提示符
命令提示符的设置就是对PS1的配置: export PS1="\[\e[35;40m\][\[\e[32;40m\]\u\[\e[37;40m\]@\[\e[36;40m\]\h \[\e ...
- 接入 SDK 结果翻车了?了解 SDK 的那些事
前段时间,二狗子的朋友圈被工信部发布的<关于下架侵害用户权益 App 名单的通报>给刷屏了.公告中指出有 90 款 App 未按照要求完成整改将会下架.而这 90 款 App 涉及全国各地 ...
- Linux安装Tomcat-Nginx-FastDFS-Redis-Solr-集群——【第十一集补充:修改fastdfs的http.conf文件进行防盗链,重启nginx失败】
1,进入fastdfs的安装目录: 2,修改http.conf文件,详情可参考: https://www.cnblogs.com/xiaolinstudy/p/9341779.html 3,重启ngi ...