断点续传的实现
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;
以上是关于断点续传的实现的主要内容,如果未能解决你的问题,请参考以下文章