大文件切片上传、实时进度条、断点续传

复制粘贴就可以实现效果,找了这么多资源终于整合了一个靠谱的了!!!!!

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script type="module" src="/node_modules/axios/dist/axios.js"></script>
  </head>
  <body>
    <input type="file" id="fileInput" />
    <!-- 设置一个进度条 -->
    <progress value="0" max="100" id="progress"></progress>
    <button onclick="upload()">上传文件</button>
    <script>
      async function upload() {
        const fileInput = document.getElementById("fileInput"); //获取input框
        const file = fileInput.files[0]; // 对input的文件进行获取
        const chunkSize = 1 * 1024 * 1024; //初始化分块的尺寸 每块分块文件大小为1MB(1兆)
        const totalChunks = Math.ceil(file.size / chunkSize); //通过文件尺寸计算出所有的块数
        let currentChunk = 0; //设置块的初始值
        const resumeKey = file.name + "-currentChunk"; // 用于断点续传的本地存储键名
        const resumeIndex = localStorage.getItem(resumeKey); // 当前的索引值
        if (resumeIndex != null) {
          currentChunk = parseInt(resumeIndex); // 如果存在已处理的分片索引,则更新currentChunk
        } else {
          localStorage.removeItem(resumeKey); // 否则移除本地存储的键值对
        }
        // 通过while循环处理
        while (currentChunk < totalChunks) {
            console.log('currentChunk',currentChunk);
            
          const start = currentChunk * chunkSize; // 计算当前块的起始位置
          const end = Math.min(start + chunkSize, file.size); // 计算当前块的结束  Math.min:返回一组数值中的最小值
          const chunk = file.slice(start, end); // 切割文件获取当前块

          const formData = new FormData();
          formData.append("file", chunk); // 添加当前块到 FormData 对象
          formData.append("filename", file.name); // 添加文件名到 FormData 对象
          formData.append("totalChunks", totalChunks); // 添加总块数到 FormData 对象
          formData.append("currentChunk", currentChunk); // 添加当前块数到 FormData 对象
          try {
            const res = await axios.post(
              "http://localhost:3000/upload",
              formData,
              {
                headers: {
                  "Content-Type": "multipart/form-data",
                },
              }
            ); //发送当前块的上传请求
            const { progress } = res.data; // 获取当前块的上传进度
            document.getElementById("progress").value = progress; // 更新进度
            // 保存当前分块的索引
            localStorage.setItem(resumeKey, currentChunk);
            
          } catch (error) {
            console.error(error);
            return;
          }
          currentChunk++; //增加当前块数,继续下一块的上传(实现循环操作)
        }
        // 上传完成后,移除localstorage中的分块索引
        localStorage.removeItem(resumeKey);
        // 当所有分块文件发送完毕,发起合并请求操作
        try {
          const postData = { filename: file.name, totalChunks: totalChunks }; //构造合并请求的数据
          await axios.post("http://localhost:3000/merge", postData, {
            headers: {
              "Content-Type": "application/json",
            },
          }); //发送合并请求
        } catch (error) {
          console.error(error);
        }
      }
    </script>
  </body>
</html>

安装依赖

npm i express cors multer axios

服务器端

const express = require("express");
const cors = require("cors"); // 解决跨域问题
const path = require("path"); // 处理路径问题
const fs = require("fs"); // 文件读取操作
const multer = require("multer"); // 进行文件上传的操作处理
const upload = multer({ dest: "uploads/" }); // 设置文件上传地址
const bodyParser = require("body-parser"); // 实现body解析操作
const app = express();

app.use(cors());
app.use(bodyParser.json()); // 实现中间件body的应用
app.use(bodyParser.urlencoded({ extended: false })); // 实现文件路径的处理

app.listen(3000, () => {
  console.log("Server started on port 3000");
});

app.post("/upload", upload.single("file"), (req, res) => {
  console.log("uplaod!!");

  const file = req.file; // 获取上传的文件对象
  const filename = req.body.filename; // 获取文件名
  const totalChunks = parseInt(req.body.totalChunks); // 获取总块数
  const currentChunk = parseInt(req.body.currentChunk); //获取当前块数
  const chunkPath = path.join("uploads/", `${filename}-chunk-${currentChunk}`); // 设置当前文件的存储路径

  const chunkStream = fs.createReadStream(file.path); //创建读取文件块的可读流
  const writeStream = fs.createWriteStream(chunkPath); //创建写入当前块的可写流

  chunkStream.pipe(writeStream); //通过chunkStream.pipe管道操作,将读取的文件块内容通过管道写入当前块的文件

  // 对分块上传内容结束以后的事件监听
  chunkStream.on("end", () => {
    fs.unlinkSync(file.path); //读取文件块的流结束后,删除临时文件
    res.sendStatus(200); //响应上传成功的状态
  });
});

app.post("/merge", (req, res) => {
  const filename = req.body.filename; //获取文件名
  const totalChunks = parseInt(req.body.totalChunks); //获取总块数

  const mergedPath = path.join("uploads", filename); //生成合并后文件的存储路径
  const writeStream = fs.createWriteStream(mergedPath); //创建写入合并后文件的可写流

  // 合并文件块
  const mergeChunks = (index) => {
    if (index === totalChunks) {
      writeStream.end(); //所有块都合并完成后,关闭写入流
      res.sendStatus(200); //响应合并成功的状态
      return;
    }

    const chunkPath = path.join("uploads", `${filename}-chunk-${index}`); //获取当前块的存储路径
    const chunk = fs.readFileSync(chunkPath); //同步读取当前块的内容
    fs.unlinkSync(chunkPath); //删除已合并的块文件

    writeStream.write(chunk, () => {
      mergeChunks(index + 1); //递归合并下一块
    });
  };

  mergeChunks(0); //从第一块开始合并
});

最终效果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值