谈谈我对大文件上传(乞丐版)

226 阅读2分钟

web

由于自己测试用不考虑美观问题直接用 html 元素,没有在乎样式问题进度条,以及切片完成后的提示,可以提交切片,等等样式页面问题都没有去做完善:)

<div>
  <input id="file2" type="file" , name="file2" onchange="change()" />
  <button id="upLoad" type="submit" onclick="upload()">upload</button>
</div>

js 部分 我用提交数据使用 fetch 方法具体使用 请移步 mdn

具体思路如下

  1. 获取文件将其切片
  2. 将切片文件上传之后台
  3. 告知文件上传完成
// 改变 input file
async function change() {
  const [file] = document.getElementById("file2").files;
  if (!file) return;
  // console.log(file);
  let cur = 0;
  const SIZE = 1 * 1024 * 1024;
  let fileChunkList = [];
  const TOTAL = Math.ceil(file.size / SIZE);
  while (cur < file.size) {
    fileChunkList.push({ file: file.slice(cur, cur + SIZE) });
    cur += SIZE;
  }
  // console.log(fileChunkList);
  fileChunkList.forEach((value, index) => {
    let fd = new FormData();
    fd.append("files", value.file);
    fd.append("cur", index);
    fd.append("name", file.name);
    fd.append("total", TOTAL);
    fetch("upload/aaa", {
      method: "post",
      body: fd
    })
      .then(response => console.log(response))
      .catch(error => console.error("Error:", error));
  });
  alert("切片完成,并提交后台。。");
}
// 切片完成,提交按钮
function upload() {
  var data = { name: "123.mp4" };
  fetch("upload/aaa2", {
    method: "post",
    body: JSON.stringify(data), // data can be `string` or {object}!
    headers: {
      "Content-Type": "application/json"
    }
  })
    .then(response => console.log(response))
    .catch(error => console.error("Error:", error));
}

nodeJS(Express)

具体思路如下

  1. 接收 web 传来等切片文件 ,使用 formidable 插件接收 formdata 数据
  2. 将获取的 formdata 放入 arr 对象,根据 cur 进行排序,避免合并的时候合并顺序混乱
  3. 将文件合并用 pipe
// 获取提交文件
router.post("/aaa", (req, res) => {
  var form = new formidable.IncomingForm();
  form.uploadDir = __dirname + "/../videos/";
  form.keepExtensions = true;
  form.parse(req, function(err, fields, files) {
    if (err) return;
    // console.log("fields", fields);
    // console.log("files", files);

    arr.push({
      name: fields.name,
      cur: fields.cur,
      total: fields.total,
      path: files.files.path
    });

    /**
     * 排序根据 cur判断M
     * @param 0 开始
     */
    arr.sort((a, b) => a.cur - b.cur);
    // console.log(arr);
    res.send(msg.success("ok"));
  });
});
// 获取合并信息
router.post("/aaa2", (req, res) => {
  // 需要合并的数组
  const checkList = arr.filter(v => v.name === req.body.name);
  arr = arr.filter(v => v.name !== req.body.name);
  const [item] = checkList;
  // 文件名称
  const NAME = item.name;
  // 写入流
  const writeStream = fs.createWriteStream(__dirname + "/../videos/" + NAME);
  merage(checkList, writeStream);
  res.json(msg.success());
});

// *合并文件
async function merage(checkList, writeStream) {
   // *一定要同步合并 不能异步
  for (const iterator of checkList) {
    // 写入
    await pipeStream(iterator, writeStream);
  }
}
// 写入流
const pipeStream = (iterator, writeStream) =>
  new Promise(resolve => {
    const readStream = fs.createReadStream(iterator.path);
    readStream.pipe(writeStream, { end: false });
    // 读取结束 删除文件
    readStream.on("end", () => {
      // 删除文件
      fs.unlinkSync(iterator.path);
      console.log("文件已删除", iterator.path);
      resolve();
    });
  });