在本系列的前两篇文章中,分别向大家介绍了用于完成下载任务的 WebClinetWinINet 的基本用法和一些实用技巧。

今天来为大家讲述下载过程中最常遇到的断点续传问题。

首先明确一点,本文所说的断点续传特指 HTTP 协议中的断点续传,文章中讲述了实现断点续传的方法思路和关键代码,想了解更多细节的同学,请下载并查看本文附带的 demo。


工作原理

http 协议中定义了一些请求/响应头,通过组合使用这些头信息,即可实现分批下载同一文件的目的。例如,在一次 http 请求中只请求文件中的一部分数据,然后将请求到的数据保存起来,下次只需请求剩余部分的数据,当全部数据都下载到本地后再完成数据的合并工作。

http 协议指出,可以通过 http 请求中的 Range 头来指定请求数据的范围。

Range 头的使用很简单,按照如下的格式使用即可:

Range: bytes=500-999

上述意思为:只请求目标文件的第500至第999,这500个字节。

举例说明,有一个1000 字节大小的文件需要下载,第一次请求时不指定 Range 头,表示下载整个文件;但在下载完第499个字节后,下载被中断了,那么在下一次请求剩余文件时,只需要下载第500个至第999个字节的数据即可。

原理看上去很简单,但是需要考虑以下几个问题:

1. 是不是所有的 web 服务器都支持 Range 头?

2. 多次请求之间可能会间隔很长的时间,服务器上的文件发生了变化怎么办?

3. 如何保存下载的部分数据和相关信息?

4. 当我们通过字节操作把一个文件拼成原始大小后,如何验证它和源文件是一模一样的?

接下来,本文分别针对以上问题,给出解决方法。


一、如何检查服务器端是否支持 Range头?

在服务器响应请求时,会在响应头中通过 Accept-Ranges 指明是否接受请求资源的一部分数据,这里似乎有个小问题,就是不同的服务器可能返回不同的值来指明是否接受下载部分资源的请求。比较统一的做法是:当服务器不支持请求部分数据时,都会返回 Accept-Ranges: none,所以只需判断返回值是否等于 none 就可以了。

代码如下:

private static bool IsAcceptRanges ( WebResponse res )

{

if ( res.Headers["Accept-Ranges"] != null )

{

string s = res.Headers["Accept-Ranges"];

if ( s == "none" )

{

return false;

}

}

return true;

}


二、如何检查服务器端的文件是否发生了变化?

当我们在下载文件的过程中,由于网络故障等原因中断了下载过程,这时如果服务器上的文件已经变化了,那么无论如何都需要重新从头开始下载,只有当服务器上的文件没有发生变化的情况下,断点续传才有意义。

当下次需要继续下载文件时,如何确定服务器上的文件还是当初下载了一半的文件?

对于这个问题,http 响应头为我们提供了两种选择,使用 ETag 和 Last-Modified 都能完成下载任务。

先看 ETag:

The ETag response-header field provides the current value of the entity tag for the requested variant. (引自RFC2616 14.19 ETag)

简单点说 ETag 就是一个标识当前请求内容的字符串,当请求的资源发生变化后,对应的 ETag 也会变化,所以最简单的办法是,第一次请求时把响应头中的 ETag 保存下来,下次请求时做相应的比较。

代码如下:

string newEtag = GetEtag( response );

// tempFileName指已经下载到本地的部分文件内容

// tempFileInfoName指保存了Etag内容的临时文件

if ( File.Exists(tempFileName) && File.Exists(tempFileInfoName) )

{

string oldEtag = File.ReadAllText( tempFileInfoName );

if ( !string.IsNullOrEmpty(oldEtag) && !string.IsNullOrEmpty(newEtag) && newEtag == oldEtag )

{

// Etag没有变化,可以断点续传

resumeDowload = true;

}

}

else

{

if ( !string.IsNullOrEmpty(newEtag) )

{

File.WriteAllText( tempFileInfoName, newEtag );

}

}

//GetEtag函数

private static string GetEtag( WebResponse res )

{

if ( res.Headers["ETag"] != null )

{

return res.Headers["ETag"];

}

return null;

}

再看 Last-Modified:

The Last-Modified entity-header field indicates the date and time at which the origin server believes the variant was last modified. (引自RFC2616 14.29 Last-Modified)

Last-Modified 就是所请求的资源在服务器上最后一次的修改时间,使用方法和 ETag 大体相同。

不论是使用 ETag 还是 Last-Modified,都能达到检测服务器端文件是否发生变化的目的。

当然也可以同时使用这两种方法,做 double check,以便更好的实现检测目的。


三、如何保存下载的部分数据和相关信息?

这里主要是指使用 C# 进行数据和相关信息的保存操作,大体思路是如果有未下载完的文件,先将已下载数据保存在某一路径下,然后将后下载的字节数据添加到已下载文件的末尾。

详细的实现方法,请查看 demo 代码。


四、如何验证下载文件与源文件的一致性?

在断点续传的过程中,我们以 byte 为单位进行文件的下载和合并,如果下载的整个过程中出现了异常,可能最后得到的文件就和源文件不一样了,因此最好能够对下载好的文件进行一次与源文件一致性的校验,这是很重要的一步,也是最难实现的部分。之所以难以实现,是因为需要服务器端的支持,例如要求服务器端不但提供了可供下载的文件,同时还需要提供该文件的 MD5 hush。

当然,如果服务器端也是我们自己创建的,我们就可以实现服务器端方面的支持。目前已有部分产品在下载过程中提供断点续传的能力,Spread Studio表格控件就是其中之一。

Demo 下载

Winform文件下载之断点续传的更多相关文章

  1. 【SFTP】使用Jsch实现Sftp文件下载-支持断点续传和进程监控

    参考上篇文章: <[SFTP]使用Jsch实现Sftp文件下载-支持断点续传和进程监控>:http://www.cnblogs.com/ssslinppp/p/6248763.html  ...

  2. Winform文件下载之WinINet

    在C#中,除了webclient我们还可以使用一组WindowsAPI来完成下载任务.这就是Windows Internet,简称 WinINet.本文通过一个demo来介绍WinINet的基本用法和 ...

  3. iOS开发之网络编程--4、NSURLSessionDataTask实现文件下载(离线断点续传下载) <进度值显示优化>

    前言:根据前篇<iOS开发之网络编程--2.NSURLSessionDownloadTask文件下载>或者<iOS开发之网络编程--3.NSURLSessionDataTask实现文 ...

  4. iOS开发之网络编程--3、NSURLSessionDataTask实现文件下载(离线断点续传下载)

    前言:使用NSURLSessionDownloadTask满足不这个需要离线断点续传的下载需求,所以这里就需要使用NSURLSessionDataTask的代理方法来处理下载大文件,并且实现离线断点续 ...

  5. iOS 文件下载及断点续传

    ios的下载我们可以使用的方法有:NSData.NSURLConnection.NSURLSession还有第三方框架AFNetworking和ASI 利用NSData方法和NSURLConnecti ...

  6. 【转】iOS 文件下载及断点续传

    ios的下载我们可以使用的方法有:NSData.NSURLConnection.NSURLSession还有第三方框架AFNetworking和ASI 利用NSData方法和NSURLConnecti ...

  7. C# 文件下载之断点续传

    注意,本文所说的断点续传特指 HTTP 协议中的断点续传.本文主要聊聊思路和关键代码,更多细节请参考本文附带的 demo. 工作原理 HTTP 协议中定义了一些请求/响应头,通过组合使用这些头信息.我 ...

  8. php大文件下载支持断点续传

    <?php   /** php下载类,支持断点续传  *  *   Func:  *   download: 下载文件  *   setSpeed: 设置下载速度  *   getRange: ...

  9. Winform文件下载之WebClient

    最近升级了公司内部使用的一个下载小工具,主要提升了下面几点: 1. 在一些分公司的局域网中,连接不上外网 2. 服务器上的文件更新后,下载到的还是更新前的文件 3. 没有下载进度提示 4. 不能终止下 ...

随机推荐

  1. linux下的触控板手势xSwipe and tag

    这个最初是采用的touchegg.开始没有效果,后来网上发现是因为需要禁用系统的2指3指操作参考 http://askubuntu.com/questions/266057/cant-get-touc ...

  2. C语言的关键字,标示符以及数据类型

      1. 关键字 1>     关键字就是C语言提供的有特殊含义的符号,也叫做“保留字” 2>     C语言一共提供了32个关键字,这些关键字都被C语言赋予了特殊含义 auto doub ...

  3. hdoj 2022 海选女主角

    Problem Description potato老师虽然很喜欢教书,但是迫于生活压力,不得不想办法在业余时间挣点外快以养家糊口.“做什么比较挣钱呢?筛沙子没力气,看大门又不够帅...”potato ...

  4. 异常:System.Data.EvaluateException: 未找到列[District].

    异常:System.Data.EvaluateException: 未找到列[District]. 这里存在的问题不一定是说,数据源表没有该字段.此问题在于数据库字段包含空格字符.

  5. C# 实现 任意多边形切割折线算法

    1.    内容简介 本文旨在解决任意多边形切割折线,获取切割之后的折线集合. 本文实现的算法内容包括:判断两条线段是否相交,如若相交,获取交点集合.对线上的点集,按斜率方向排序.判断点是否在多边形内 ...

  6. java 基础题 很基础, 很有趣

    都是一些非常非常基础的题,是我最近参加各大IT公司笔试后靠记忆记下来的,经过整理献给与我一样参加各大IT校园招聘的同学们,纯考Java基础功底, 老手们就不用进来了,免得笑话我们这些未出校门的孩纸们, ...

  7. rdc21n(研发与设计综合讨论)博客开通了!

    rdc21n是“Research and Design Comprehensive discussioN”,其中21表示Comprehensive discussioN中间的21个字母(不包含空格), ...

  8. day14---html基础

    本节内容: 一.HTML 二.CSS 三.JS HTML 1.一套规则,浏览器认识的规则. 2.开发者: 学习Html规则 开发后台程序: - 写Html文件(充当模板的作用) ****** - 数据 ...

  9. iphone 耳机 线控

    有电话呼入时: 按一次接听电话: 快速按两次将电话转到语音信箱: 通话中: 按一次挂断电话: 通话中如果有第二个电话打进来时: 按一次保留当前通话并接听第二个电话: 按住两秒钟不放忽略(拒绝接听)第二 ...

  10. 练习1-16:修改打印最长文本行的程序的主程序main,使之可以打印任意长度的输入行的长度,并尽可能多地打印文本(C程序设计语言 第2版)

    该书英文配套答案 Answer to Exercise -, page Revise the main routine of the longest-line program so it will c ...