在本系列的前两篇文章中,分别向大家介绍了用于完成下载任务的 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. Asp.net Mvc 身份验证、异常处理、权限验证(拦截器)实现代码

    本问主要介绍asp.net的身份验证机制及asp.net MVC拦截器在项目中的运用.现在让我们来模拟一个简单的流程:用户登录>权限验证>异常处理 1.用户登录 验证用户是否登录成功步骤直 ...

  2. css3小总结

    一.边框 1.border-radius:x,y,模糊半径,color(x为负数时左移动,y为负数是上移动) 2.box-radius:x,y,模糊半径,阴影半径,color 3.border-ima ...

  3. python subprocess 自动运行实验室程序

    import threading, os, subprocess, time exec_path = "/home/xhz/gems/ruby/amd...../bin/tester.exe ...

  4. 前台js与后台方法互调

    一:后台调用前台js的方法 ClientScript.RegisterStartupScript(ClientScript.GetType(), "myscript", " ...

  5. Swift - mutating关键字的使用

    转载自:http://www.jianshu.com/p/14cc9d30770a  感谢作者:此ID想了很久 Swift中protocol的功能比OC中强大很多,不仅能再class中实现,同时也适用 ...

  6. iOS 利用webView加载html代码,在代理中获取html页面的链接时出现的问题

    getDetailWebview.loadHTMLString(webViewData as String, baseURL:NSURL(string: "\(ProBaseWeb)&quo ...

  7. tomcat 假死现象(转)

    1.1 编写目的 为了方便大家以后发现进程假死的时候能够正常的分析并且第一时间保留现场快照. 1.2编写背景 最近服务器发现tomcat的应用会偶尔出现无法访问的情况.经过一段时间的观察最近又发现有台 ...

  8. CodeForces 743B Chloe and the sequence (递归)

    题意:给定n和k,求第n个序列中第k个数是多少,序列是这样构造,原来只有1,然后再copy一遍放在到后面再在中间放上一个没有出现过的最小整数,就变成了 121,下次就成了1213121. 析:很明显是 ...

  9. java程序操作Geometry对象

    Geometry 空间地理对象,Oracle中存储Geometry对象的字段类型是 MDSYS.SDO_GEOMETRY,在数据库中构建Geometry对象的方法: v_pointarray MDSY ...

  10. html入门问题_2016-10-29

    在mac机器上,用Safari打开html文件 1. 如果html里有中文,则在<head><meta http-equiv="Content-Type" con ...