node实现分片下载的示例代码


Posted in Javascript onOctober 17, 2018

本文基于http Range Requests协议,实现了分片下载的功能。

使用场景包括基于浏览器的流文件片段传输、基于客户端的分片下载等。

原理

http通过Range Requests相关的header,可以与服务器进行协商,实现分部分的请求。

这里就不细说具体协议内容了,具体可以参考这两篇文章,解释的非常详细:

  1. https://tools.ietf.org/html/rfc7233
  2. https://3water.com/article/68284.htm

下面贴一下实现过程。

服务端代码

服务端用node实现:

app.use(async ctx => {
 const file = path.join(__dirname, `${PATH}${ctx.path}`);
 // 1、404检查
 try {
  fs.accessSync(file);
 } catch (e) {
  return ctx.response.status = 404;
 }
 const method = ctx.request.method;
 const { size } = fs.statSync(file);
 // 2、响应head请求,返回文件大小
 if ('HEAD' == method) {
  return ctx.set('Content-Length', size);
 }
 const range = ctx.headers['range'];
 // 3、通知浏览器可以进行分部分请求
 if (!range) {
  return ctx.set('Accept-Ranges', 'bytes');
 }
 const { start, end } = getRange(range);
 // 4、检查请求范围
 if (start >= size || end >= size) {
  ctx.response.status = 416;
  return ctx.set('Content-Range', `bytes */${size}`);
 }
 // 5、206分部分响应
 ctx.response.status = 206;
 ctx.set('Accept-Ranges', 'bytes');
 ctx.set('Content-Range', `bytes ${start}-${end ? end : size - 1}/${size}`);
 ctx.body = fs.createReadStream(file, { start, end });
});

app.listen(3000, () => console.log('partial content server start'));

function getRange(range) {
 var match = /bytes=([0-9]*)-([0-9]*)/.exec(range);
 const requestRange = {};
 if (match) {
  if (match[1]) requestRange.start = Number(match[1]);
  if (match[2]) requestRange.end = Number(match[2]);
 }
 return requestRange;
}

代码实现的功能逻辑大致是:

  • 对请求的资源做检查,不存在则响应404
  • 对于HEAD请求,返回资源大小
  • 如果GET请求没有告知range,返回Content-Length,告知浏览器可以进行分片请求
  • 如果请求设置了range,则检查range是否合法,不合法返回合法的rangge
  • 一切正常,获取文件range范围部分,做流响应

代码很简单,把Range Requests协议对应实现一遍就ok了,当然这里没有完全实现协议的内容,但已经满足了这里演示的需求。

服务端代码ok了,用一个浏览器的demo来检验一下。

浏览器例子

现代浏览器基本都实现了Range Requests,这里用audio标签作为例子。

<html>
 <head>
  <title>分片流传输</title>
  <script type="text/javascript">
   function jump() {
    const player = document.getElementById('musicPlayer');
    // 从30s开始播放
    player.currentTime = 30;
   }
  </script>
 </head>
 <body>
  <audio id="musicPlayer" src="http:127.0.0.1:3000/source.mp3" controls></audio>
  <button onclick="jump()">切到30s</button>
 </body>
</html>

最终的效果是这样的:

node实现分片下载的示例代码

node实现分片下载的示例代码

对比两张图,当html加载完成,浏览器自动请求资源,此时header有Range: bytes=0-,表示从第0 byte开始加载资源;当点击跳到30s处播放时,此时header变成了Range: bytes=3145728-

同样用这个服务端代码,还可以实现一个客户端,模拟一下分包下载。

node分包下载

这个例子演示了,对一个资源,并发的实现分部分的下载,然后再合并成一个文件。

这里也是用node实现:

import request from 'request';
import path from 'path';
import fs from 'fs';

const SINGLE = 1024 * 1000;
const SOURCE = 'http://127.0.0.1:3000/source.mp3';

request({
 method: 'HEAD',
 uri: SOURCE,
}, (err, res) => {
 if (err) return console.error(err);
 const file = path.join(__dirname, './download/source.mp3');
 try {
  fs.closeSync(fs.openSync(file, 'w'));
 } catch (err) {
  return console.error(err);
 }
 const size = Number(res.headers['content-length']);
 const length = parseInt(size / SINGLE);
 for (let i=0; i<length; i++) {
  let start = i * SINGLE;
  let end = i == length ? (i + 1) * SINGLE - 1 : size - 1;
  request({
   method: 'GET',
   uri: SOURCE,
   headers: {
    'range': `bytes=${start}-${end}`
   },
  }).on('response', (resp) => {
   const range = resp.headers['content-range'];
   const match = /bytes ([0-9]*)-([0-9]*)/.exec(range);
   start = match[1];
   end = match[2];
  }).pipe(fs.createWriteStream(file, {start, end}));
 }
});

代码比较简单,就是开启多个http请求,并发的下载资源,然后根据响应的content-range,写到文件的对应位置。

参考文章:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jQuery 全选效果实现代码
Mar 23 Javascript
js跑马灯代码(自写)
Apr 17 Javascript
js模仿hover的具体实现代码
Dec 30 Javascript
JQuery限制复选框checkbox可选中个数的方法
Apr 20 Javascript
深入浅析JavaScript字符串操作方法 slice、substr、substring及其IE兼容性
Dec 16 Javascript
JS实现隐藏同级元素后只显示JS文件内容的方法
Sep 04 Javascript
JS判断form内所有表单是否为空的简单实例
Sep 09 Javascript
什么是JavaScript中的结果值?
Oct 08 Javascript
vue中如何实现pdf文件预览的方法
Jul 12 Javascript
javaScript实现游戏倒计时功能
Nov 17 Javascript
vue中注册自定义的全局js方法
Nov 15 Javascript
JavaScript 获取滚动条位置并将页面滑动到锚点
Feb 08 Javascript
在小程序开发中使用npm的方法
Oct 17 #Javascript
浅谈HTTP 缓存的那些事儿
Oct 17 #Javascript
使用angular-cli webpack创建多个包的方法
Oct 16 #Javascript
element-ui的回调函数Events的用法详解
Oct 16 #Javascript
基于vue-upload-component封装一个图片上传组件的示例
Oct 16 #Javascript
Postman的下载及安装教程详解
Oct 16 #Javascript
Vue.js 时间转换代码及时间戳转时间字符串
Oct 16 #Javascript
You might like
php中计算程序运行时间的类代码
2012/11/03 PHP
php中的字符编码转换函数用法示例
2014/10/20 PHP
IE浏览器兼容Firefox的JS脚本的代码
2008/10/23 Javascript
Javascript remove 自定义数组删除方法
2009/10/20 Javascript
jQuery 常见学习网站与参考书
2009/11/09 Javascript
Javascript 面向对象之重载
2010/05/04 Javascript
javascript学习笔记(一) 在html中使用javascript
2012/06/18 Javascript
13 个JavaScript 性能提升技巧分享
2012/07/26 Javascript
解析如何利用iframe标签以及js制作时钟
2016/12/08 Javascript
jquery实现全选、全不选以及单选功能
2017/03/23 jQuery
浅谈在react中如何实现扫码枪输入
2018/07/04 Javascript
在Vue methods中调用filters里的过滤器实例
2018/08/30 Javascript
vue基础之v-bind属性、class和style用法分析
2019/03/11 Javascript
微信小程序如何调用json数据接口并解析
2019/06/29 Javascript
layui动态渲染生成左侧3级菜单的方法(根据后台返回数据)
2019/09/23 Javascript
javascript实现前端分页效果
2020/06/24 Javascript
解决vue init webpack 下载依赖卡住不动的问题
2020/11/09 Javascript
Python中optionParser模块的使用方法实例教程
2014/08/29 Python
python模拟enum枚举类型的方法小结
2015/04/30 Python
Python中subprocess的简单使用示例
2015/07/28 Python
python3.6下Numpy库下载与安装图文教程
2019/04/02 Python
Python 离线工作环境搭建的方法步骤
2019/07/29 Python
matplotlib jupyter notebook 图像可视化 plt show操作
2020/04/24 Python
Python 如何创建一个线程池
2020/07/28 Python
Python如何创建装饰器时保留函数元信息
2020/08/07 Python
用Python实现童年贪吃蛇小游戏功能的实例代码
2020/12/07 Python
python实现马丁策略回测3000只股票的实例代码
2021/01/22 Python
美国羊皮公司:Overland
2018/01/15 全球购物
C/C++程序员常见面试题一
2012/12/08 面试题
护理自荐信
2013/10/22 职场文书
公关关系专员的自我评价分享
2013/11/20 职场文书
活动总结报告范文
2014/05/04 职场文书
教师四风对照检查材料思想汇报
2014/09/17 职场文书
人身损害赔偿协议书格式
2014/11/01 职场文书
Nginx进程管理和重载原理详解
2021/04/22 Servers
Nginx源码编译安装过程记录
2021/11/17 Servers