断点续传的实现

从上次断开位置继续下载

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;
	     }
    }
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值