欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue.js+express利用切片實(shí)現(xiàn)大文件斷點(diǎn)續(xù)傳

 更新時(shí)間:2023年05月17日 09:54:30   作者:小小熒  
斷點(diǎn)續(xù)傳就是要從文件已經(jīng)下載的地方開(kāi)始繼續(xù)下載,本文主要介紹了Vue.js+express利用切片實(shí)現(xiàn)大文件斷點(diǎn)續(xù)傳,具有一定的參考價(jià)值,感興趣的可以了解下

斷點(diǎn)續(xù)傳

在文件上傳期間因?yàn)橐恍┦虑橹袛?比如網(wǎng)絡(luò)中斷,服務(wù)器出錯(cuò),客戶端奔潰),但是在下次上傳同一個(gè)文件可以從上一次上傳的位置繼續(xù)上傳,以節(jié)省上傳時(shí)間。

github-demo

實(shí)現(xiàn)思路

  • 將文件上傳分為多個(gè)切片
  • 上傳切片
  • 合并切片
  • 文件切片驗(yàn)證
  • 狀態(tài)的切換

1.文件如何在前端分切片

我們需要為文件生成唯一fileHash,目的是為了每次上傳判斷此文件是否之前存在過(guò)斷點(diǎn),如果hash相同同時(shí)存在斷點(diǎn),那么就需要斷點(diǎn)續(xù)傳,如果上傳成功過(guò)就直接重新上傳即可。

使用Blob對(duì)象中的slice函數(shù)可以進(jìn)行對(duì)文件進(jìn)行切片處理??梢詫⑽募袨樽远x的大小和數(shù)量.

Blob.slice

創(chuàng)建一個(gè)createChunl函數(shù)

const createFileChunk = async (file, size = SIZE) => {
  const fileChunkList = [];
  let spark = new SparkMd5.ArrayBuffer();
  let readerComplete = false;
  const reader = new FileReader();
  for (let cur = 0; cur < file.size; cur += size) {
    let data = { file: file.slice(cur, cur + size) };
    fileChunkList.push(data);
    reader.readAsArrayBuffer(data.file);
    await new Promise((resolve) => {
      reader.onload = (e) => {
        spark.append(e.target.result);
        resolve();
      };
    });
  }
  fileHash.value = spark.end();
  return fileChunkList;
};

spark-md5是一個(gè)高效的md5加密算法,詳情請(qǐng)看文檔spark-md5,通過(guò)spark-md5將文件內(nèi)容加密為一個(gè)hash值,用來(lái)作為文件上傳的唯一標(biāo)識(shí)。

創(chuàng)建一個(gè)upload函數(shù)

const handleUpload = async () => {
  if (!uploadFile.value) return;
  loading.value = true;
  const fileChunkList = await createFileChunk(uploadFile.value);
  let vertifyRes = await request({
    url: "/vertify",
    method: "post",
    data: {
      fileHash: fileHash.value,
      filename: fileHash.value + "." + getSuffix(uploadFile.value.name),
    },
  });
  if (!vertifyRes.data.shouldUpload) {
    alert(vertifyRes.msg);
    uploadPercentage.value = 100;
    loading.value = false;
    return;
  }
  data.value = fileChunkList.map(({ file }, index) => ({
    chunk: file,
    hashPrefix: fileHash.value,
    suffix: getSuffix(uploadFile.value.name),
    hash: fileHash.value + "-" + index,
    index: index,
    percentage: 0,
  }));
  await uploadChunk(vertifyRes.data.uploadList);
  loading.value = false;
};

上傳文件切片,首先需要驗(yàn)證hash值是否在服務(wù)端已經(jīng)存在如果存在實(shí)際上相同的文件已經(jīng)存在了,當(dāng)效驗(yàn)未通過(guò)時(shí)說(shuō)明不需要再上傳了,只有當(dāng)效驗(yàn)通過(guò)是才可以上傳文件,驗(yàn)證接口的響應(yīng)數(shù)據(jù)中會(huì)帶有切片的hash值,為了是過(guò)濾掉已上傳的切片提高上傳效率。

剩下的前端只需要過(guò)濾hash的切片,將剩下的切片上傳即可

// 上傳切片
const uploadChunk = async (uploadList = []) => {
  const requestList = data.value
    .filter(({ hash }) => !uploadList.includes(hash))
    .map(({ chunk, hash, index, hashPrefix }) => {
      const formData = new FormData();
      formData.append("chunk", chunk);
      formData.append("hash", hash);
      formData.append("filename", uploadFile.value.name);
      formData.append("index", index);
      formData.append("hashPrefix", hashPrefix);
      return { formData, index };
    })
    .map(async ({ formData, index }) => {
      return request({
        url: "/upload_chunk",
        data: formData,
        onUploadProgress: onPregress.bind(null, data.value[index]),
        cancelToken: new axios.CancelToken((c) => {
          aborts.value.push(c);
        }),
        headers: {
          "Content-Type": "multipart/form-data",
        },
        method: "post",
      });
    });
  showStopAndResume.value = true;
  await Promise.all(requestList);
  // 合并切片
  await mergeRequest();
  aborts.value = [];
};

這里的mergeRequest是當(dāng)所有的切片上傳成功后,要告訴后端進(jìn)行切片合并。

const mergeRequest = async () => {
  let res = await request({
    url: "/merge_chunk",
    headers: {
      "Content-Type": "application/json",
    },
    method: "post",
    data: {
      originName: uploadFile.value.name,
      filename: fileHash.value + "." + getSuffix(uploadFile.value.name),
      size: SIZE,
    },
  });
};

這樣前端一些核心的代碼基本完成了。

2.文件如何在后端實(shí)現(xiàn)切片上傳

后端暫且使用express實(shí)現(xiàn)

根據(jù)前端的要求有以下幾個(gè)接口需要實(shí)現(xiàn)

  • /upload_chunk
  • /merge_chunk
  • /vertify

實(shí)現(xiàn)思路

1./upload_chunk

利用fs相關(guān)api將上傳的緩存chunk資源移動(dòng)到對(duì)應(yīng)目錄中

const multipart = new multiparty.Form({
    maxFieldsSize: 200 * 1024 * 1024,
  });
  multipart.parse(req, async (err, fields, files) => {
    if (err) {
      res.send({
        code: 500,
        msg: "服務(wù)器錯(cuò)誤",
      });
      return;
    }
    const [chunk] = files.chunk;
    const [hash] = fields.hash;
    const [hashPrefix] = fields.hashPrefix;
    const chunkDir = path.resolve(UPLOAD_DIR, hashPrefix);
    if (!fse.existsSync(chunkDir)) {
      await fse.mkdirs(chunkDir);
    }
    await fse.move(chunk.path, `${chunkDir}/${hash}`);
    res.send({
      code: 200,
      msg: "上傳成功",
    });
  });

2./merge_chunk

將上傳的chunk按照hash-index序號(hào)進(jìn)行順序合并,用到比較核心的api就是fs.createReadStream()

// 合并切片
const mergeFileChunk = async (filePath, filename, size) => {
  const chunkDir = path.resolve(
    UPLOAD_DIR,
    filename.slice(0, filename.lastIndexOf("."))
  );
  const chunkPaths = await fse.readdir(chunkDir);
  // 根據(jù)切片下標(biāo)進(jìn)行排序
  // 否則直接讀取目錄的獲取的順序可能會(huì)錯(cuò)亂
  chunkPaths.sort((a, b) => a.split("-")[1] - b.split("-")[1]);
  let pipeP = chunkPaths.map((chunkPath, index) =>
    pipeStream(
      path.resolve(chunkDir, chunkPath),
      fse.createWriteStream(filePath, {
        start: index * size,
        end: (index + 1) * size,
      })
    )
  );
  await Promise.all(pipeP);
  fse.rmdirSync(chunkDir); // 合并后刪除保存的切片目錄
};
const pipeStream = (path, writeStream) => {
  return new Promise((resolve) => {
    const readStream = fse.createReadStream(path);
    readStream.on("end", () => {
      fse.unlinkSync(path);
      resolve();
    });
    readStream.pipe(writeStream);
  });
};
app.post("/merge_chunk", async (req, res) => {
  //   size是每一個(gè)chunk的size
  const { filename, size } = req.body;
  const filePath = path.resolve(UPLOAD_DIR, `${filename}`);
  await mergeFileChunk(filePath, filename, size);
  res.send({
    code: 200,
    msg: "file merged success",
  });
});

創(chuàng)建chunk切片路徑的讀入流,然后將讀入流寫入新的地址,當(dāng)所有切片全部寫入完成,完整的文件就合并成功了。

3./vertify

這個(gè)api比較簡(jiǎn)單,用來(lái)查看當(dāng)前文件是否存在服務(wù)器資源目錄中,存在返回shouldUpload為false,不存在就需要獲取此文件上傳的切片數(shù)組用于過(guò)濾前端不需要上傳的切片

const createUploadedList = async (fileHash) =>
  fse.existsSync(path.resolve(UPLOAD_DIR, fileHash))
    ? await fse.readdir(path.resolve(UPLOAD_DIR, fileHash))
    : [];
app.post("/vertify", async (req, res) => {
  const { filename, fileHash } = req.body;
  const filePath = path.resolve(UPLOAD_DIR, filename);
  if (fse.existsSync(filePath)) {
    res.send({
      code: 200,
      data: {
        shouldUpload: false,
      },
      msg: "文件已上傳成功",
    });
    return;
  }
  res.send({
    code: 200,
    data: {
      shouldUpload: true,
      uploadList: await createUploadedList(fileHash),
    },
    msg: "請(qǐng)求成功",
  });
});

到此,大文件上傳基本工作就已經(jīng)完成了。其中最核心的思想就是切片思想,將大的文件進(jìn)行唯一標(biāo)識(shí)+切片上傳,服務(wù)器進(jìn)行先緩存切片在進(jìn)行合并操作。保證了斷點(diǎn)可續(xù)傳,大文件穩(wěn)定上傳。

到此這篇關(guān)于Vue.js+express利用切片實(shí)現(xiàn)大文件斷點(diǎn)續(xù)傳的文章就介紹到這了,更多相關(guān)Vue express 大文件斷點(diǎn)續(xù)傳內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 手寫?Vue3?響應(yīng)式系統(tǒng)(核心就一個(gè)數(shù)據(jù)結(jié)構(gòu))

    手寫?Vue3?響應(yīng)式系統(tǒng)(核心就一個(gè)數(shù)據(jù)結(jié)構(gòu))

    這篇文章主要介紹了手寫?Vue3?響應(yīng)式系統(tǒng)(核心就一個(gè)數(shù)據(jù)結(jié)構(gòu)),響應(yīng)式就是被觀察的數(shù)據(jù)變化的時(shí)候做一系列聯(lián)動(dòng)處理。就像一個(gè)社會(huì)熱點(diǎn)事件,當(dāng)它有消息更新的時(shí)候,各方媒體都會(huì)跟進(jìn)做相關(guān)報(bào)道。這里社會(huì)熱點(diǎn)事件就是被觀察的目標(biāo)
    2022-06-06
  • dataV大屏在vue中的使用方式

    dataV大屏在vue中的使用方式

    這篇文章主要介紹了dataV大屏在vue中的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • 淺談 Vue 項(xiàng)目?jī)?yōu)化的方法

    淺談 Vue 項(xiàng)目?jī)?yōu)化的方法

    這篇文章主要介紹了淺談 Vue 項(xiàng)目?jī)?yōu)化的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-12-12
  • 修改el-form-item中的label里面的字體邊距或者大小問(wèn)題

    修改el-form-item中的label里面的字體邊距或者大小問(wèn)題

    這篇文章主要介紹了修改el-form-item中的label里面的字體邊距或者大小問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • 手把手教你使用vue-cli腳手架(圖文解析)

    手把手教你使用vue-cli腳手架(圖文解析)

    本篇文章主要介紹了手把手教你使用vue-cli腳手架(圖文解析),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-11-11
  • vue如何搭建多頁(yè)面多系統(tǒng)應(yīng)用

    vue如何搭建多頁(yè)面多系統(tǒng)應(yīng)用

    這篇文章主要為大家詳細(xì)介紹了vue搭建多頁(yè)面多系統(tǒng)應(yīng)用的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-06-06
  • vue3?hook自動(dòng)導(dǎo)入原理解析

    vue3?hook自動(dòng)導(dǎo)入原理解析

    這篇文章主要介紹了vue3?hook自動(dòng)導(dǎo)入的原理,介紹了API的自動(dòng)導(dǎo)入及組件的自動(dòng)導(dǎo)入,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-09-09
  • Vue項(xiàng)目通過(guò)vue-i18n實(shí)現(xiàn)國(guó)際化方案(推薦)

    Vue項(xiàng)目通過(guò)vue-i18n實(shí)現(xiàn)國(guó)際化方案(推薦)

    這篇文章主要介紹了Vue項(xiàng)目通過(guò)vue-i18n實(shí)現(xiàn)國(guó)際化方案,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-12-12
  • vue3的二維碼組件vue3-next-qrcode

    vue3的二維碼組件vue3-next-qrcode

    這篇文章主要為大家介紹了vue3的二維碼組件vue3-next-qrcode示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • Vue基礎(chǔ)教程之條件渲染和列表渲染

    Vue基礎(chǔ)教程之條件渲染和列表渲染

    Vue會(huì)盡可能高效地渲染元素,通常會(huì)復(fù)用已有元素而不是從頭開(kāi)始渲染。這么做會(huì)使Vue變得非???下面這篇文章主要給大家介紹了Vue基礎(chǔ)教程之條件渲染和列表渲染的相關(guān)資料,需要的朋友可以參考下
    2021-11-11

最新評(píng)論