C#编程断点续传
C#编程总结(十二)断点续传
我们经常使用下载工具,如bit精灵、迅雷、FlashGet,这些软件都支持断点续传。
断点续传即下载任务暂停后可以继续,而无需重新下载,即下载时需要通知服务器的起始位置。如果允许多线程进行分片下载,必须提供起始-截止位置。说到底就是可以选择下载某个片段,整个文件的字节流,可以截取流的片段,也能实现流的累积,最终完成文件下载。
一、原理
在 HTTP/1.1里新增的一个头属性:Range,也是现在众多号称多线程下载工具(如 FlashGet、迅雷等)实现多线程下载的核心所在。老版本的HTTP协议不支持,所以一些老的服务器还不支持断点续传。
Range(请求参数)
用于请求头中,指定第一个字节的位置和最后一个字节的位置,一般格式:
Range:(unit=first byte pos)-[last byte pos]
例如:Range:100-199,取文件流的100至199之间的字节。
Range:100,取位置为100后的所有字节。如果range 为正值,服务器应该开始发送从指定的 range 参数到 HTTP 实体中数据的末尾之间的数据。
Range:-99,取开始的100个字节。如果range 为负值,服务器应该开始发送从 HTTP 实体中数据的开头到指定的 range 参数之间的数据。
Content-Range (响应参数)
用于响应头,指定整个实体中的一部分的插入位置,他也指示了整个实体的长度。在服务器向客户返回一个部分响应,它必须描述响应覆盖的范围和整个实体长度。
一般格式:
Content-Range: bytes (unit first byte pos) - [last byte pos]/[entity legth]
例如:Content-Range: bytes 1024000-1126399/7421120
HTTP协议:http://www.w3.org/Protocols/rfc2616/rfc2616.html
二、C#中实现
在C#中使用AddRange方法向请求添加指定范围的字节范围标头
System.Net.HttpWebRequest
所有的方法:
常用的方法为例:
void AddRange(long from, long to);
指定范围起始、终止位置,来请求该片段的数据。
// 摘要:
// 向请求添加指定范围的字节范围标头。
//
// 参数:
// from:
// 开始发送数据的位置。
//
// to:
// 停止发送数据的位置。
public void AddRange(long from, long to);
我们通过该方法,基于HTTP协议实现了断点续传,支持暂停、继续下载功能,为了更清晰显示效果,提供了进度条显示。效果图:
Request
请求的Range参数,我们可以清晰看到起具体值,这就是请求的片段。在这里可以清晰的看到HTTP协议版本、请求方法、请求地址等信息
Response
服务器根据请求的Range参数,只返回该片段的数据。我们可以清晰看到Content-Range的具体值。
注意:返回的StatusCode变为了PartialContent,说明是部分数据。
代码 含义
200 OK 请求成功返回
206 Partial Content 部分数据
下载的核心代码:
/// <summary>
/// 下载
/// </summary>
public void Download()
{
//从0计数,需要减一
long from = this.currentSize;
if (from < 0)
{
from = 0;
} long to = this.currentSize + this.step - 1;
if (to >= this.totalSize && this.totalSize > 0)
{
to = this.totalSize - 1;
}
this.Download(from, to);
}
/// <summary>
/// 下载
/// </summary>
/// <param name="url"></param>
/// <param name="range"></param>
public void Download(long from,long to)
{
if (this.totalSize == 0)
{
GetTotalSize();
}
if (from >= this.totalSize || this.currentSize >= this.totalSize)
{
this.isFinished = true;
return;
} HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
//request.Method = "GET";
request.AddRange("bytes", from, to); HttpWebResponse response = (HttpWebResponse)request.GetResponse();
string result = string.Empty;
if (response != null)
{
byte[] buffer = this.Buffer;
using (Stream stream = response.GetResponseStream())
{
int readTotalSize = 0;
int size = stream.Read(buffer, 0, buffer.Length);
while (size > 0)
{
//只将读出的字节写入文件
fs.Write(buffer, 0, size);
readTotalSize += size;
size = stream.Read(buffer, 0, buffer.Length);
} //更新当前进度
this.currentSize += readTotalSize; //如果返回的response头中Content-Range值为空,说明服务器不支持Range属性,不支持断点续传,返回的是所有数据
if (response.Headers["Content-Range"] == null)
{
this.isFinished = true;
}
}
}
}
项目源码:
http://files.cnblogs.com/yank/DownloadSample.rar
三、多线程下载
上述例子提供了简单的断点续传功能,如果想再进一步实现多线程下载。原理很简单,我们只需根据所要下载的文件大小,进行分块,没块启动一个线程进行下载,线程只负责下载自己负责的片段,一定要严格设置Range的值。具体实现这里不再介绍,如有兴趣,下来可以继续研究。
提醒一下:多线程下载的字节流如何保存为文件,是否必须按照先后顺序?
最新文章
- zabbix自定义key
- Linux下通过脚本自动备份Oracle数据库并删除指定天数前的备份
- Setup QT 5.5.1 + OpenCV 3.0 + Visual Studio 2013 on windows 10
- json 入门(1)
- 探究platform_driver中的shutdown用途
- 【转】十分钟搞定pandas
- Selenium2学习-035-WebUI自动化实战实例-033-页面快照截图应用之三 -- 区域截图(专业版)
- java 与 R 相互调用
- iOS-音频格式转换-b
- 使用ajax代替iframe
- Java获取文件大小的正确方法(转)
- JAVA 年老代收集器 第10节
- node.js Setup Wizard ended prematurely 安装失败
- Vue2.2版本学习小结
- 【BZOJ3379】【USACO2004】交作业 区间DP
- 如何禁止复制电脑文件到U盘、禁止U盘拷贝文件
- linux下用命令修改文件内容
- winform 凹进去的button
- webpack4--热更新
- JSON草稿
热门文章
- 使用reserve要再次避免不必要的分配
- STUN协议简介
- NYNU_省赛选拔题(6)
- Unity插件之NGUI学习(8)—— Table和NGUI尺寸转换为世界坐标系尺寸
- javascript系列之核心知识点(一)
- 【高德地图API】从零开始学高德JS API(二)地图控件与插件——测距、圆形编辑器、鼠标工具、地图类型切换、鹰眼鱼骨
- 搭建一个BS 的简单SOA 架构(直接通过jquery 调用后台的 wcf 服务的架构)(第一天)
- net.sf.json 迄今 时刻 格式 办法
- C#中设计Fluent API
- T4模板生成不同部署环境下的配置文件