本文基于http请求范围协议,实现了分片下载的功能。
使用场景包括基于浏览器的流文件片段传输,基于客户端的分片下载等。
<强>原理强>
http通过请求范围相关的头,可以与服务器进行协商,实现分部分的请求。
这里就不细说具体协议内容了,具体可以参考这两篇文章,解释的非常详细:
-
<李> https://tools.ietf.org/html/rfc7233 李>
<李> https://www.jb51.net/article/68284.htm 李>
下面贴一下实现过程。
<>强服务端代码强>
服务端用节点实现:
app.use(异步ctx=比;{ 常量文件=路径。加入(__dirname“$ {PATH} $ {ctx.path}”);//1404检查 尝试{ fs.accessSync(文件); }捕捉(e) { ctx.response返回。状态=404; } 常量方法=ctx.request.method; const{大小}=fs.statSync(文件);//2、响应头请求,返回文件大小 如果(“头”==方法){ ctx返回。集(内容长度,大小); } 常量范围=ctx.headers(的范围);//3、通知浏览器可以进行分部分请求 如果范围(!){ ctx返回。设置(“Accept-Ranges”、“字节”); } const{开始,结束}=getRange(范围);//4、检查请求范围 如果(开始在大?| |结束祝辞=大小){ ctx.response。状态=416; ctx返回。集(“含量”、“字节*/${大小}”); }//5206分部分响应 ctx.response。状态=206; ctx。设置(“Accept-Ranges”、“字节”); ctx。设置(“含量”、“字节{开始}-{美元结束& # 63;结束:尺寸- 1}/${大小}'); ctx。身体=fs。createReadStream(文件,{开始,结束}); }); app.listen(3000年,()=比;控制台。日志(部分内容服务器开始')); 函数getRange(范围){ var=/字节=匹配([0 - 9]*)- ([0 - 9]*)/.exec(范围); const requestRange={}; 如果(匹配){ 如果requestRange(匹配[1])。比赛开始=数量([1]); 如果requestRange(匹配[2])。结束=数量(匹配[2]); } 返回requestRange; }
代码实现的功能逻辑大致是:
-
<李>对请求的资源做检查,不存在则响应404年李>
<李>对于头请求,返回资源大小李>
<李>如果得到请求没有告知范围,返回内容长度,告知浏览器可以进行分片请求李>
<李>如果请求设置了范围,则检查范围是否合法,不合法返回合法的rangge李>
<李>一切正常,获取文件范围范围部分,做流响应李>
代码很简单,把范围请求协议对应实现一遍就好了,当然这里没有完全实现协议的内容,但已经满足了这里演示的需求。
服务端代码好了,用一个浏览器的演示来检验一下。
<强>浏览器例子强>
现代浏览器基本都实现了范围的请求,这里用音频标签作为例子。
& lt; html> & lt; head> & lt; title>分片流传输& lt;/title> & lt;脚本type=" text/javascript祝辞 函数跳转(){ const球员=. getelementbyid (“musicPlayer”);//从30年代开始播放 的球员。currentTime=30; } & lt;/script> & lt;/head> & lt; body> & lt;音频id=" musicPlayer " src=" https://www.yisu.com/zixun/http 127.0.0.1:3000/source.mp3”controls> & lt;/audio> & lt; button>切到30 s & lt;/body> & lt;/html>
最终的效果是这样的:
对比两张图,当html加载完成,浏览器自动请求资源,此时头有<代码>范围:字节=0 -> 代码,表示从第0字节开始加载资源,当点击跳到30年代处播放时,此时头变成了<代码>范围:字节=3145728 -> 代码。
同样用这个服务端代码,还可以实现一个客户端,模拟一下分包下载。
<强>节点分包下载强>
这个例子演示了,对一个资源,并发的实现分部分的下载,然后再合并成一个文件。
这里也是用节点实现:
从“请求”进口请求; 从“路径”导入路径; 进口fs“fs”; const单=1024 * 1000; const源=' http://127.0.0.1:3000 source.mp3 '; 请求({ 方法:“头”, uri:源, },(呃,res)=比;{ 如果(err)返回console.error(错); 常量文件=路径。加入(__dirname。/下载/source.mp3); 尝试{ fs.closeSync (fs。openSync(文件、' w ')); }捕捉(err) { 返回console.error (err); } 常量大?数量(res.headers['内容长度']); const长度=方法(大?单); (让我=0;i节点实现分片下载的示例代码