文件分块+断点续传 实现大文件上传全栈解决方案(前端+nodejs)

avatar
作者
猴君
阅读量:1

1. 文件分块

将大文件切分成较小的片段(通常称为分片或块),然后逐个上传这些分片。这种方法可以提高上传的稳定性,因为如果某个分片上传失败,只需要重新上传该分片而不需要重新上传整个文件。同时,分片上传还可以利用多个网络连接并行上传多个分片,提高上传速度。

2. 断点续传

在上传过程中,如果网络中断或上传被中止,断点续传技术可以记录已成功上传的分片信息,以便在恢复上传时继续上传未完成的部分,而不需要重新上传整个文件。这种技术可以大大减少上传失败的影响,并节省时间和带宽。

3. node项目目录初始化

在这里插入图片描述

  1. 安装依赖

    • express 敏捷启动服务
    • multer 读取文件,存储
    • cors 解决跨域
  2. 目录结构

    • src
      • TED.mp4 (长视频,10分钟,可以下载这个 https://mirror.aarnet.edu.au/pub/TED-talks/911Mothers_2010W-480p.mp4
    • uploads 存放切片
    • video 存放将切片拼接后的视频
    • index.html 前端页面

代码附上

index.html

<!DOCTYPE html> <html lang="en">   <head>     <meta charset="UTF-8" />     <meta name="viewport" content="width=device-width, initial-scale=1.0" />     <title>Document</title>   </head>   <body>     <input id="file" type="file" />     <script>       // functions       // 实现切片的方法       const makeChunk = (file, size = 1024 * 1024 * 4) => {         const chunks = [];         for (let i = 0; i < file.size; i += size) {           const chunk = file.slice(i, i + size);           chunks.push(chunk);         }         return chunks;       };        // 上传分片后的文件方法       const uploadChunks = (chunks) => {         // 1. 使用Promise.all保证所有上传方法执行成功         // 2. 必须要给每个分片文件加标识,才可以让node端进行按序拼接         const list = [];         for (let i = 0; i < chunks.length; i++) {           const formData = new FormData();           formData.append("filename", "ted");           formData.append("index", i);           formData.append("chunk", chunks[i]); // 千万注意,切片文件要最后append否则会出现意外的bug            list.push(             fetch("http://localhost:3000/upload", {               method: "POST",               body: formData,             })           );         }          Promise.all(list)           .then((res) => {             console.log("上传成功", res);             fetch("http://localhost:3000/merge", {               method: "POST",               headers: {                 "Content-Type": "application/json",               },               body: JSON.stringify({                 fileName: "TED演讲10分钟长视频",               }),             });           })           .catch((err) => {             console.error("上传失败", err);           });       };        // logic       const file = document.querySelector("#file");       file.addEventListener("change", (e) => {         let file = e.target.files[0]; // 我们只处理单个文件因此取第一个元素即可         // file是一个对象,底层继承于Blob,借助于Blob身上的slice方法,可以实现对大文件的分片操作         console.log(file);         const chunks = makeChunk(file);         console.log("chunks:", chunks);         uploadChunks(chunks);       });     </script>   </body> </html>  

index.js

import fs from "node:fs"; import path from "node:path"; import express from "express"; import multer from "multer"; import cors from "cors";  console.log("cors:", cors);  // 1. 初始化multer const storage = multer.diskStorage({   // 指定切片存放目录   destination: function (req, file, cb) {     cb(null, "../uploads/");   },   filename: function (req, file, cb) {     console.log("req.body.index:", req.body.index);     console.log("file", file);     cb(null, `${req.body.filename}-${req.body.index}`);   }, });  const upload = multer({ storage });  const app = express(); app.use(cors()); app.use(express.json());  // upload.single("chunk") 其中chunk对应前端上传的文件切片名字 app.post("/upload", upload.single("chunk"), (req, res) => {   console.log("req.body:", req.body);   res.header("Content-Type", "application/json;charset=utf-8");   res.send("ok"); });  // 拼接切片 app.post("/merge", (req, res) => {   // 获取uploadDir的目录   const uploadDir = path.join(process.cwd(), "../uploads");   const dirs = fs.readdirSync(uploadDir); // 发现是乱序的   // 对dirs数组进行排序   dirs.sort((a, b) => {     return a.split("-")[1] - b.split("-")[1];   });   const videoDir = path.join(     process.cwd(),     "../video",     `${req.body.fileName}.mp4`   );    dirs.forEach((item) => {     // 合并成一个完整的文件至video目录     fs.appendFileSync(videoDir, fs.readFileSync(path.join(uploadDir, item)));     // 删除已合并的切片文件     fs.unlinkSync(path.join(uploadDir, item));   });   console.log(dirs);   res.send("okk"); });  app.listen(3000, () => {   console.log("server is running at port 3000"); });  

最终效果

在这里插入图片描述
在这里插入图片描述

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!