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

主要技术点
切片
- 利用
Blob.prototype.slice切片 - 获取切片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});
如何运行
- 安装依赖
1npm install
- 运行
1npm run dev
- 浏览器打开
1http://127.0.0.1:9870
相关资料
浏览器文件MD5加密
async mapLimit
前端大文件上传
