使用 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, // 要下载的文件分片数量,默认值为1MaximumBytesPerSecond = 1024 * 1024, // 下载速度限制为1MB/s,默认值为零或无限制MaxTryAgainOnFailover = int.MaxValue, // 失败的最大次数OnTheFlyDownload = false, // 是否在内存中进行缓存? 默认值是trueParallelDownload = true, // 下载文件是否为并行的。默认值为falseTempDirectory = "C:\\temp", // 设置用于缓冲大块文件的临时路径,默认路径为Path.GetTempPath()。Timeout = 1000, // 每个 stream reader 的超时(毫秒),默认值是1000RequestConfiguration = // 定制请求头文件{Accept = "*/*",AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,CookieContainer = new CookieContainer(), // Add your cookiesHeaders = new WebHeaderCollection(), // Add your custom headersKeepAlive = false,ProtocolVersion = HttpVersion.Version11, // Default value is HTTP 1.1UseDefaultCredentials = 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# 下载文件的十八般武艺相关推荐
- ☀️手把手教你用 C# 下载文件的十八般武艺☀️《❤️记得收藏❤️》
☀️手把手教你用 C# 下载文件的十八般武艺☀️<❤️记得收藏❤️> 目录
- 用python下载文件的若干种方法汇总
压缩文件可以直接放到下载器里面下载的 you-get 连接 下载任意文件 重点 用python下载文件的若干种方法汇总 写文章 用python下载文件的若干种方法汇总 zhangqibot发表于Met ...
- 初级版python登录验证,上传下载文件加MD5文件校验
服务器端程序 import socket import json import struct import hashlib import osdef md5_code(usr, pwd):ret = ...
- linux快捷上传下载文件
借助securtCRT,使用linux命令sz可以很方便的将服务器上的文件下载到本地,使用rz命令则是把本地文件上传到服务器 其中,对于sz和rz的理解与记忆我用了如下的方法(因为很多时候容易搞混): ...
- 基于php下载文件的详解
基于php下载文件的详解 本篇文章是对php下载文件进行了详细的分析介绍,需要的朋友参考下 php下载文件,比如txt文件. 出现的效果就是,弹出浏览器自带的下载框,出现另存为操作.有时候会出现内存溢 ...
- java上传加密_Java上传下载文件并实现加密解密
使用 Jersey 服务器实现上传,使用 HTTP 请求实现下载 引入依赖 在 pom.xml 中添加 Jersey 相关依赖 com.sun.jersey jersey-client 1.18.1 ...
- sftp shell 批量上传文件_Shell自动上传下载文件到SFTP服务器
1.说明 本文提供一个Shell脚本, 可以自动连接到SFTP服务器, 然后上传或者下载指定的文件, 进而可以使用Linux的corntab命令, 定时执行脚本上传下载文件, 实现文件的同步或者备份功 ...
- php 当前页面下载文件,php实现当前页面点击下载文件的简单方法
php实现当前页面点击下载文件的简单方法 发布于 2017-08-02 17:44:21 | 80 次阅读 | 评论: 0 | 来源: 网友投递 PHP开源脚本语言PHP(外文名: Hypertext ...
- ASP.NET 下载文件方式
protected void Button1_Click(object sender, EventArgs e){/*微软为Response对象提供了一个新的方法TransmitFile来解决使用Re ...
最新文章
- kali linux samba,Kali Linux 渗透测试:SMB、SMTP扫描工具(14)
- XML文档DOM、SAX、STAX解析方式
- Cloudify — Plugins
- 【温故知新】HTML学习笔记(下)
- pyecharts第五节、关系图
- 使用idea创建JavaWeb项目
- 二进制、八进制、十六进制相互转换
- Helm 3 完整教程(十三):Helm 函数讲解(7)列表函数
- 华为的手册和官网视频,学习网络基础
- 数据操作(基于MXNET框架)
- Nessus高级使用研究
- 数据分析与可视化概述
- ember helper
- 华硕CSM不能设置解决方案
- elasticsearch collapse
- python str和repr的区别_python str与repr的区别
- tomcat 中部署的应用响应json数据乱码解决办法
- Civil 3D 2012 SP 2.1发布了
- Calendar判断指定时间是周几、上下午、月份的简单运用
- 完美解决Word、Excel、PPT加密解密的方法
热门文章
- C#摄像头实现拍照功能的简单代码示例
- python3用list实现栈
- 在ubuntu 16.04里使用python—scrapy将爬取到的数据存到mysql数据库中的一些随笔
- Jzoj5317 Func
- ReactNative--React简介
- Mustache.js使用笔记(内容属于转载总结)
- 设计模式(10)-----模板方法模式
- Framer – 将视觉搞转换为更真实的动态原型
- mac命令行将输出写入文件_如何在Linux中使用命令行将PDF文件转换为可编辑文本...
- 腾讯地图判断点是否在区域内