C#实现文件断点续传下载的方法
本文介绍了如何利用 C# 中的 HttpWebRequest 和 FileStream 实现支持断点续传的下载功能。核心逻辑在于识别本地已存在文件的长度,并在发起 HTTP 请求时添加 Range 头以指定偏移量,从而实现从断点处继续下载。
核心原理与准备工作
断点续传的核心依赖于 HTTP 协议的 Range 请求头。客户端在检测到本地已有部分下载内容时,读取该文件的大小作为字节偏移量,通过设置 Request.AddRange(offset) 告知服务器只需发送从该位置开始的数据。服务端响应后,客户端将流数据以追加方式写入本地文件,而非覆盖重写,以此节省流量并支持中断恢复。
为实现此功能,主要依赖以下基础库:
System.Net:用于创建和管理 HTTP 请求 (HttpWebRequest)。System.IO:用于处理文件流的读写 (FStream、StreamReader)。System.Text:处理编码格式判断(如 UTF-8、Unicode 等)。
第1步:构建通用 HTTP 请求封装类
为了便于管理请求参数及复用 Range 设置逻辑,建议将网络请求封装为独立的静态工具类。
- 目的:统一创建带有特定起始位置偏移的 HTTP 请求实例。
- 入口/动作:定义一个名为
getWebRequest的公共静态方法。 - 关键参数:
-url: 目标下载链接。
-lStartPos: 起始字节偏移量,即断点位置,默认为 0 表示从头下载。 - 操作细节:调用
HttpWebRequest.Create(url)创建对象,随即执行request.AddRange(lStartPos)。
using System;using System.Net;using System.IO;namespace simpleDemo{ class httpClient { /// <summary> /// 获取带Range参数的WebRequest /// </summary> public static HttpWebRequest getWebRequest(string url, int lStartPos) { HttpWebRequest request = null; try { request = (System.Net.HttpWebRequest)HttpWebRequest.Create(url); request.AddRange(lStartPos); // 设置断点续传的关键 } catch (Exception ex) { Console.WriteLine(ex.Message); } return request; } }}此步骤确保了每次调用下载函数时,都能准确携带上一次的中断位置信息。
第2步:下载主流程与状态判断
下载过程需要判断文件是否存在。如果不存在,直接新建;如果存在,则需要进入续传逻辑。本示例采用了一个小技巧:始终先下载到一个带有特定后缀的临时文件(例如 .cdel),待下载全部完成后,再将其重命名为主文件名。
- 准备:设定远程 URL 和本地保存路径。
- 逻辑分支 A - 全新下载:
- 若文件不存在,则调用下载逻辑,目标路径为path + ".cdel"。 - 逻辑分支 B - 断点续传:
- 若文件已存在,打开该文件获取当前长度lStartPos。 - 获取总大小校验:发送 HEAD 或 GET 请求获取服务端的
Content-Length。 - 完成判定:比较
response.ContentLength与lStartPos。如果两者相等,说明本地文件已经是完整的,直接重命名完成退出,避免无效请求。
public static void simpleDownload(string url, string path){ HttpWebRequest request = httpClient.getWebRequest(url, 0); WebResponse response = null; // 以追加模式打开文件 FileStream writer = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write); long lStartPos = writer.Length; long currentLength = lStartPos; long totalLength = 0; if (File.Exists(path)) { // 检查断点状态 response = request.GetResponse(); long sTotal = response.ContentLength; // 如果当前大小等于总大小,视为已完成 if (sTotal == lStartPos) { close(writer); File.Move(path, path.Replace(PERSIST_EXP, "")); Console.WriteLine("下载完成!"); return; } // 重新获取针对当前位置的 Request request = httpClient.getWebRequest(url, (int)lStartPos); writer.Seek(lStartPos, SeekOrigin.Begin); response = request.GetResponse(); totalLength = response.ContentLength + lStartPos; } else { response = request.GetResponse(); totalLength = response.ContentLength; } // ... 后续读取流代码 }第3步:流式读取与进度更新
这是实际数据传输的阶段。从响应的 Stream 中按块读取字节数组,并写入 FileStream。
- 输入:
response.GetResponseStream()和网络缓冲区byte[]。 - 动作:在
while ((c = reader.Read(buff, 0, buff.Length)) > 0)循环中不断读取。 - 记录:累加已读取字节数
currentLength += c,并调用writer.Write()写入磁盘。 - 进度显示:根据
currentLength和totalLength计算百分比并打印控制台日志。
Stream reader = response.GetResponseStream();byte[] buff = new byte[1024];int c = 0;while ((c = reader.Read(buff, 0, buff.Length)) > 0) { currentLength += c; writer.Write(buff, 0, c); progressBar(currentLength, totalLength); writer.Flush();}这一过程的稳定性对于大文件至关重要。频繁调用 Flush() 虽有助于实时落盘减少数据丢失风险,但在网络极慢时可能影响性能,可视情况调整缓冲策略。
第4步:收尾工作与重命名
当数据流读取完毕,需确保所有资源被正确释放,并将临时文件还原。
- 资源清理:关闭
reader、writer和response,防止句柄泄漏。 - 完整性校验:判断最终的
currentLength是否等于预期的totalLength。 - 文件更名:若一致,调用
File.Move(path, path.Replace(PERSIST_EXP, ""))去除后缀,正式生效。
close(writer); // 释放 writer 资源if (currentLength == totalLength) { File.Move(path, path.Replace(PERSIST_EXP, "")); Console.WriteLine("下载完成!");}else { Console.WriteLine("下载失败!");}辅助功能:文件编码检测
虽然主要用于文本分析,但原代码中包含了一段通过检测文件前缀字节 (BOM) 来判断文本编码的逻辑。这涉及到对 byte 数组的前几个字节进行比对:
0xEF, 0xBB, 0xBF->UTF-80xFF, 0xFE->Unicode(UTF-16 LE)0xFE, 0xFF->BigEndianUnicode(UTF-16 BE)
这段代码通过 stream.ReadByte() 逐位读取并转换,展现了如何在缺乏显式声明时底层探查数据属性。
总结与排错
要实现稳健的断点续传,除了正确使用 Range 头和追加写入外,还需注意以下几点:
- 服务端支持度:某些特殊的 CDN 或代理服务器可能不支持 Range 请求,导致
AddRange报错或失效。 - 异常捕获:在网络中断时,务必处理好
SocketException或WebException,保留好中间状态文件。 - 并发控制:同一文件不应同时启动多个相同路径的下载进程,否则会导致锁冲突或逻辑混乱。
以上就是 C# 实现文件断点续传下载的详细内容,更多关于 C# 网络编程的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













