手摸手,带你完成大文件分片下载 您所在的位置:网站首页 佛头少普中要缩小当前文件的视图可以执行哪些操作 手摸手,带你完成大文件分片下载

手摸手,带你完成大文件分片下载

#手摸手,带你完成大文件分片下载| 来源: 网络整理| 查看: 265

前言

之前看到,一篇大文件上传的文章,感觉上传里面的细节考虑还是很多的。

字节跳动面试官:请你实现一个大文件上传和断点续传 - 掘金 (juejin.cn)

然后最近就在钻研一下文件下载的功能

描述的不好还请各位大佬见谅,萌新第一次写文章。😁

项目地址:

react 及乐1997/react-demo - 码云 - 开源中国 (gitee.com)

node big-files: nodejs的大文件分片上传下载以及秒传 (gitee.com)

整体思路 获取要下载文件的大小,然后除以分片的大小,明确要发送几次网络请求 后端获取请求头里面的range字段 前端把请求回来的切片做一个合并 然后创建一个URL.createObjectURL来下载 后端 express

我是用了两个接口,一个是获取文件大小的,一个是下载的

router.get('/size/:name',(req,res)=>{ //获取要下载文件的路径 let filePath = path.resolve(__dirname,distPath,req.params.name) //console.log(filePath) //获取文件的大小 let size = fs.statSync(filePath).size || null console.log('下载文件大小' + size) res.send({ msg:'ok', data:size.toString() }) }) 复制代码

下载分为三种情况,一种是不需要分片 直接去下载,第二种是 请求分片的开始位置和结束位置不对,拒绝请求, 第三种是 位置正确 返回分片给前端

router.get("/down/:name", (req, res) => { let filename = req.params.name; //获取文件的位置 和文件的大小 let filePath = path.resolve(__dirname, distPath, req.params.name); let size = fs.statSync(filePath).size; //获取请求头的range字段 let range = req.headers["range"]; let file = path.resolve(__dirname, distPath, filename); //不使用分片下载 直接传输文件 if (!range) { //res.set({'Accept-Ranges':'bytes'}) res.set({ "Content-Type": "application/octet-stream", "Content-Disposition": `attachment; filename=${filename}`, }); fs.createReadStream(file).pipe(res); return; } //获取分片的开始和结束位置 let bytesV = range.split("="); bytesV.shift() let [start, end] = bytesV.join('').split("-"); start = Number(start) end = Number(end) //分片开始 结束位置不对 拒绝下载 if (start > size || end > size) { res.set({ "Content-Range": `bytes */${size}`}); res.status(416).send(null); return; } //开始分片下载 res.status(206); res.set({ "Accept-Ranges": "bytes", "Content-Range": `bytes ${start}-${end ? end : size}/${size}`, }); console.log(start + '---' + end) fs.createReadStream(file, { start, end }).pipe(res); }); 复制代码 前端 react //视图 setname(e.target.value)} /> 下载 复制代码 downfile //下载文件 SIZE为分片大小 const SIZE = 200 * 1024 * 1024; //设置切片的大小 const downfile = async () => { let contentLength = await filesize(name); let chunks = Math.ceil(contentLength / SIZE); let chunksl = [...new Array(chunks).keys()]; //用流的方式操作不行 但是如果后端把文件变成压缩包 每个分片都是一个压缩包 应该就可以 此种方法 下载下来是多个压缩包 然后压缩包数量正确才能解压 有兴趣的可以看一下streamSaver这个库 可以实现边读边下 /* for (let i of chunksl) { let start = i * SIZE; let end = i + 1 === chunks ? contentLength : (i + 1) * SIZE - 1; let res = await getBinaryContent(start, end, i); let fileStream = streamSaver.createWriteStream(name,{flags:'a',start}); if (res.data.stream().pipeTo) { //这个用axios请求的 时候responseType设置为"blob" blob有stream这个方法 await res.data.stream().pipeTo(fileStream); } } */ //大文件下载 要控制并发数量 此处偷懒 使用了asyncPool这个库 let results = await asyncPool(3,chunksl,(i)=>{ let start = i * SIZE; let end = i + 1 === chunks ? contentLength : (i + 1) * SIZE - 1; return getBinaryContent(start, end, i); }) results.sort((a,b)=>a.index - b.index) let arr = results.map(r=>r.data) //多个blob排序完合并为一个blob let buffers = new Blob(arr) saveAs(name,buffers) }; 复制代码 filesize //获取要下载文件的大小 const filesize = async (name) => { let res = await http.get(`/size/${name}`); return res.data; }; 复制代码 getBinaryContent //根据传入的参数发起范围请求 const getBinaryContent = async (start, end, i) => { let result = await http.get(`down/${name}`, { headers: { Range: `bytes=${start}-${end}` }, responseType: "blob", }); return { index: i, data: result }; }; 复制代码 saveAs //保存文件 const saveAs = ( name, buffers, mime = "application/octet-stream" ) => { const blob = new Blob([buffers], { type: mime }); const blobUrl = URL.createObjectURL(blob); const a = document.createElement("a"); a.download = name a.href = blobUrl; a.click(); URL.revokeObjectURL(blob); }; 复制代码 参考资料

JavaScript 中如何实现大文件并行下载? - 掘金 (juejin.cn)

前端大文件下载方案_azurecho-CSDN博客_前端大文件下载



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有