断点续传的实现

Posted 黑桃_K_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了断点续传的实现相关的知识,希望对你有一定的参考价值。

文章目录

从上次断开位置继续下载

HTTP断点续传:

实现原理(客户端):

1.客户端保存当前已下载文件的位置或大小。
2.在下次请求时告诉服务端自己请求的是哪一区间的数据。
3.服务端根据请求中的范围,从文件指定位置取出区间范围的数据进行响应。
关键问题:上次下载文件跟本次断点续传请求文件不一致(上次断开后,文件数据发生改变)
一旦上次断开后文件数据发生了改变,就算客户端请求的是区间数据,也为了防止数据不一致重新返回完整的文件数据

HTTP实现(流程):

1.服务端对客户的响应中加:	Accept-Ranges:bytes  	表示自己支持断点续传。
		同时需要在响应中加入: ETag:“文件唯一标识符”  (判断文件是否发生变化)

2.客户端在断点续传时,通过字段if-Range:Etag的值,告诉服务端文件是否发生改变,如果没改变则断点续传,改变则下载完整。

客户端在断点续传时,通过字段Range:bytes=200-1000,2000-3423,5000- 告诉服务器请求那部分数据。如图1:

3.服务器根据断点续传中的if-Rang 和 Range 字段确定是否可以断点续传,如果可以
(1)文件没有改变,可以续传,则响应206表示部分传输,并且正文是部分文件数据
(2)文件改变,不能续传,则响应200表示这是新的下载,下载全部文件数据 

并且服务器响应中,携带字段Content-Range:bytes 200-1000/78978 表示当前响应数据是那一部分数据(语法如图2)

图1: 客户端 Range头部信息

图2:服务端Content-Range信息

客户端与服务端的请求与响应图解:


获取文件 ETag 的函数:

GetIndentifer 函数作用:
				利用文件的**大小**和**文件最后一次修改时间**拼接起来
				返回拼接字符串;作为 ETag
static std::string GetIndentifer(const std::string& path)     
	uint64_t mtime, fsize;    
    fsize = fs::file_size(path);    
    auto time_type = fs::last_write_time(path);    
    mtime = decltype(time_type)::clock::to_time_t(time_type);    
    std::stringstream ss;    
    ss << fsize << mtime ;    
    std::cout<<ss.str()<<"\\n";    
    return ss.str();    
 

下载文件(断点续传) 的函数:

Download 函数的作用(取Download中的断点续传部分):
				下载函数在页面展示之后,可以通过页面中文件显示点击下载;
					1.通过请求方法获取到文件名称;
					2.添加文件路径+文件名;
					3.调用GetIndentifer函数获取文件的ETag
					4.判断请求方法中是否有If-Range的头部字段;
						有则表示支持断点续传:
					5.判断请求方法中的 If-Range对应的ETag  与当前的ETag是否相同
						相同则表示文件没有发生改变;
						不相同表示文件发生改变,则从头下载;
					6.从请求方法获取需要续传的文件起始位置和结束位置。
					7.从文件中读取到相应位置的数据放入响应正文;
					8.设置头部信息,设置响应状态码为206 ,表示区间数据响应成功;
static void Download(const httplib::Request& req,httplib::Response& rsp)    
    std::cout<<"Download ......\\n";
    std::string name=req.matches[1];
    std::string pathname=BACKUP_PATH+name;
    std::string newetag=GetIndentifer(pathname);
    uint64_t fsize=fs::file_size(pathname);
    
    if(req.has_header("If-Range"))
	    std::string oldetag=req.get_header_value("If-Range");
	    if(oldetag==newetag)
	        //断点区间,获取数据范围
	        std::cout<<req.ranges[0].first<<" - "<<req.ranges[0].second<<"\\n";
	        int64_t start=req.ranges[0].first;
	        int64_t end=req.ranges[0].second;
	        Util::RangeRead(pathname,&rsp.body,&start,&end);
	        rsp.set_header("Content-Type","application/octet-stream");
	        rsp.set_header("ETag",newetag);
	        std::stringstream ss;
	        ss<<"bytes "<<start<<"-"<<end<<"/"<<fsize;//   /后面跟文件大小,不知道大小可以写 *                                                                                   
	        std::cout<<ss.str()<<std::endl;
	        rsp.set_header("Content-Range",ss.str());
	        rsp.status=206;
	        return;
	     
    


创作挑战赛 新人创作奖励来咯,坚持创作打卡瓜分现金大奖

以上是关于断点续传的实现的主要内容,如果未能解决你的问题,请参考以下文章

断点续传实现

断点续传的实现

java文件断点续传的简单实现

局域网超大文件上传和断点续传的实现

Python实现支持并发断点续传的FTP

断点续传的实现