大文件分块(切片)断点上传

之前看过相关文章但是一直没有动手实现,这个东西就是为了实现这个而产生的。

前端流程图

主要技术点

切片

  1. 利用 Blob.prototype.slice 切片
  2. 获取切片md5 作为唯一标识

具体代码

1// 计算切片数量 2const page = Math.ceil(file.size / size); 3// 初始位置 4let start = 0; 5// 等待的异步任务队列 6const promiseList = []; 7for (let i = 0; i < page; i++) { 8 // 计算当前切片的终止位置 9 const end = start + size; 10 const item = file.slice(start, end); 11 // 更新 开始位置 12 start = end; 13 // 生成单个块 (异步) 14 // const block = generateItemBlock(item, i); 15 const block = { 16 key:md5, // md5 17 index:i, // index 18 data: item // 切片数据 19 }; 20}

合并

读取切片文件获取文件Buffer
利用Buffer.concat([b1,b2])合并切片

1/** 2 * 合并切片成文件 3 * @param {String} filename 文件名 4 * @param {Array} keys 切片id(临时文件名)列表 5 */ 6async function mergeFile (filename, keys) { 7 const readFile = util.promisify(fs.readFile); 8 // 读取文件 9 const files = keys.map(key => { 10 return readFile(path.join(__dirname, '../files/', key)); 11 }); 12 // 异步等待结果 13 const filesBuffer = await Promise.all(files); 14 // NOTE: 合并切片 15 const buffer = Buffer.concat(filesBuffer); 16 // 写入 17 fs.writeFileSync(path.join(__dirname, '../files/', filename), buffer); 18 // 移除切片文件 19 keys.forEach(key => { 20 fs.unlink(path.join(__dirname, '../files/', key), function (err) { 21 if (err) { 22 throw err; 23 } 24 }) 25 }); 26}

文件容器管理

使用lowdb存储文件信息,使用文件md5作为文件id

接口介绍

POST /fileStatus 接口

ReqData
1{ 2 "key": "", // 文件Md5 3 "length": 0, // 切片数量 4 "name": "" // 文件名称 5}
ResData
1{ 2 "key": "", // 文件Md5 3 "length": 0, // 切片数量 4 "filename": "", // 文件名称 5 "blocks": [], // 已完成的切片列表 6 "url"?: "" // 文件M地址 7}

容器数据格式

1{ 2 "key": "", // md5 3 "length": 0, // 切片数量 4 "filename": "", // 文件名 5 "blocks": [] // 已完成切片数量 6}

切片信息

1{ 2 "index": 0, // 切片下标 3 "key": "" // 切片 md5 我这里作同时作为切片文件名 4}

切片管理

切片上传成功后需要管理标示,根据切片下标判断切片位置,通过文件容器判断切片上传状态,再将切片整合

接口介绍

POST /uploadBlock 接口

ReqData

Form Data

1key: 切片id 2index: 下标 3file: 切片数据 4parent: 文件容器id
ResData
1{ 2 "code":200 3}

异步请求优化

使用async mapLimit限制异步任务数量,避免一次性请求数量过多后导致进行中的请求过多,避免中断的时候浪费资源过多

1// 控制异步并发,避免块数量过多失败的时候导致成功数量变少 2// 限制并发为3个 3async.mapLimit(blocks, 3, async (file, callback) => { 4 // 调用上传 块 5 await uploadBlcok(file); 6}, async () => { 7 // 单任务执行完成后获取当前文件状态,可在这里获取文件地址 8});

如何运行

  1. 安装依赖
1npm install
  1. 运行
1npm run dev
  1. 浏览器打开
1http://127.0.0.1:9870

相关资料

浏览器文件MD5加密
async mapLimit
前端大文件上传

end

blog

https://github.com/notbucai/examples

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

一篇文章带你了解JavaScript日期

日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用