大文件上传

Category:

Tags:

大文件上传

后端实现

  1. 上传文件路由

    text
    testRouter.post("/up", upload.any(), async (ctx, next) => {  await sleep(Math.random() * 10000 + 2000);  // 保存文件  fs.writeFileSync(    `./rebase/${ctx.request.files[0].fieldname}`,    ctx.request.files[0].buffer );  ctx.body = "/abc success"; });
  2. 合并文件路由

    1. appendFileSync:用于将给定数据同步附加到文件。如果不存在,将创建一个新文件。
    text
    testRouter.post("/merge", async (ctx, next) => {  const { name } = ctx.request.body;  // 1. 获取所有分片  const chunks = fs.readdirSync("./rebase");  // 2. 拼接文件名字  const currTime = new Date();  const fileName = `${currTime.getFullYear()}_${    currTime.getMonth() + 1  }_${currTime.getDate()}_${Date.now().toString().slice(-5)}_${Math.ceil(    Math.random() * 100000 )}`;  // 3. 便利文件通过appendFile整合文件  for (let k of chunks) {    fs.appendFileSync(      `./upload/${fileName}.${name.split(".")[1]}`,      fs.readFileSync(`./rebase/${k}`)   );    // 4. 删除该文件    fs.unlinkSync(`./rebase/${k}`); }  ctx.body = "/merge success"; });

前端实现

  1. 布局

    text
       <input type="file" id="btn" @change="ced" />    <span id="upload" @click="send">上传</span>
  2. 实现ced和send

    1. FileReader:是一个对象,其唯一目的是从 Blob(因此也从 File)对象中读取数据。

      它使用事件来传递数据,因为从磁盘读取数据可能比较费时间。

      • readAsArrayBuffer(blob) —— 将数据读取为二进制格式的 ArrayBuffer
      • onload读取完成事件回调
    2. SparkMD5:用于校验字符串或者文件,以防止文件、字符串被“篡改”。因为如果文件、散列值不一样,说明文件内容也是不一样的

    text
    const ced = (e) => {  // 1.拿到上传的文件  const file = e.target.files[0];  console.log(file);  fileInfo = file; }; const send = () => {  // 2.前端校验  const { size, type } = fileInfo;  // if (size > 20 * 1024 * 1024) {  //   alert("文件过大");  //   btn.value = "";  //   return;  // }  // if (type.split("/")[0] !== "image") {  //   alert("文件类型错误");  //   btn.value = "";  // }  // 3.获取文件buffer  const fileRead = new FileReader();  fileRead.readAsArrayBuffer(fileInfo);  // 4.监听转换完成  fileRead.onload = (res) => {    // 5.转换完成获取文件buffer    const buf = res.target.result;    // 6.通过文件内容获取唯一hash    const sp5 = new SparkMD5.ArrayBuffer();    sp5.append(buf);    const hash = sp5.end(); ​    // 7.分割大文件 5m为一片,buf数组保存到partList    const partSize = 5 * 1024 * 1024;    const pageNum = Math.ceil(fileInfo.size / partSize);    let current = 0;    const partList = [];    for (let i = 0; i < pageNum; i++) {      let reqItem = {        chunk: fileInfo.slice(current, current + partSize),        filename: `${hash}_${i}`,     };      current += partSize;      partList.push(reqItem);   }    // 8.创建切片请求    let count = 0;    for (let item of partList) {      // 9.构建formdata      const formData = new FormData();      formData.append(item.filename, item.chunk);      // 10.并行发送上传请求      axios.post("http://localhost:1000/test/up", formData).then((res) => {        count++;        // 所有片段上传成功,合并服务器碎片为完整文件        if (count === pageNum) {          axios           .post("http://localhost:1000/test/merge", { name: "a.mp4" })           .then((res) => {              console.log(res);           });       }        console.log(res);     });   } }; };