最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript大文件上傳的處理方法之切片上傳

 更新時(shí)間:2022年06月28日 10:28:15   作者:? Dddusty?  ?  
這篇文章主要介紹了JavaScript大文件上傳的處理方法之切片上傳,切片上傳的原理較為簡(jiǎn)單,即獲取文件后切片,切片后整理好每個(gè)切片的參數(shù)并發(fā)請(qǐng)求即可

前言

本篇介紹了切片上傳的基本實(shí)現(xiàn)方式(前端),以及實(shí)現(xiàn)切片上傳后的一些附加功能,切片上傳原理較為簡(jiǎn)單,代碼注釋比較清晰就不多贅述了,后面的附加功能介紹了實(shí)現(xiàn)原理,并貼出了在原本代碼上的改進(jìn)方式。有什么錯(cuò)誤希望大佬可以指出,感激不盡。

切片后上傳

切片上傳的原理較為簡(jiǎn)單,即獲取文件后切片,切片后整理好每個(gè)切片的參數(shù)并發(fā)請(qǐng)求即可。

下面直接上代碼:

HTML

<template>
  <div>
    <input type="file" @change="handleFileChange" />
    <el-button @click="handleUpload">上傳</el-button>
  </div>
</template>

JavaScript

<script>
const SIZE = 10 * 1024 * 1024; // 切片大小

export default {
  data: () => ({
    // 存放文件信息
    container: {
      file: null
      hash: null
    },
    data: [] // 用于存放加工好的文件切片列表
    hashPercentage: 0 // 存放hash生成進(jìn)度
  }),
  methods: {
    // 獲取上傳文件
    handleFileChange(e) {
      const [file] = e.target.files;
      if (!file) {
        this.container.file = null;
        return;
      }
      this.container.file = file;
    },
        
    // 生成文件切片
    createFileChunk(file, size = SIZE) {
     const fileChunkList = [];
      let cur = 0;
      while (cur < file.size) {
        fileChunkList.push({ file: file.slice(cur, cur + size) });
        cur += size;
      }
      return fileChunkList;
    },
        
    // 生成文件hash    
    calculateHash(fileChunkList) {
      return new Promise(resolve => {
        this.container.worker = new Worker("/hash.js");
        this.container.worker.postMessage({ fileChunkList });
        this.container.worker.onmessage = e => {
          const { percentage, hash } = e.data;
          // 可以用來(lái)顯示進(jìn)度條
          this.hashPercentage = percentage;
          if (hash) {
            resolve(hash);
          }
        };
      });
    },

   	// 切片加工(上傳前預(yù)處理 為文件添加hash等)
    async handleUpload() {
      if (!this.container.file) return;
      // 切片生成
      const fileChunkList = this.createFileChunk(this.container.file);
      // hash生成
      this.container.hash = await this.calculateHash(fileChunkList);
      this.data = fileChunkList.map(({ file },index) => ({
        chunk: file,
        // 這里的hash為文件名 + 切片序號(hào),也可以用md5對(duì)文件進(jìn)行加密獲取唯一hash值來(lái)代替文件名
        hash: this.container.hash + "-" + index
      }));
      await this.uploadChunks();
    }
      
    // 上傳切片
   	async uploadChunks() {
     const requestList = this.data
     	// 構(gòu)造formData
       .map(({ chunk,hash }) => {
         const formData = new FormData();
         formData.append("chunk", chunk);
         formData.append("hash", hash);
         formData.append("filename", this.container.file.name);
         return { formData };
       })
     	// 發(fā)送請(qǐng)求 上傳切片
       .map(async ({ formData }) =>
       	request(formData)
       );
     await Promise.all(requestList); // 等待全部切片上傳完畢
     await merge(this.container.file.name) // 發(fā)送請(qǐng)求合并文件
   	},
  }
};
</script>

生成hash

無(wú)論是前端還是服務(wù)端,都必須要生成文件和切片的 hash,之前我們使用文件名 + 切片下標(biāo)作為切片 hash,這樣做文件名一旦修改就失去了效果,而事實(shí)上只要文件內(nèi)容不變,hash 就不應(yīng)該變化,所以正確的做法是根據(jù)文件內(nèi)容生成 hash,所以我們修改一下 hash 的生成規(guī)則

這里用到另一個(gè)庫(kù) spark-md5,它可以根據(jù)文件內(nèi)容計(jì)算出文件的 hash 值,另外考慮到如果上傳一個(gè)超大文件,讀取文件內(nèi)容計(jì)算 hash 是非常耗費(fèi)時(shí)間的,并且會(huì)引起 UI 的阻塞,導(dǎo)致頁(yè)面假死狀態(tài),所以我們使用 web-worker 在 worker 線程計(jì)算 hash,這樣用戶仍可以在主界面正常的交互

由于實(shí)例化 web-worker 時(shí),參數(shù)是一個(gè) js 文件路徑且不能跨域,所以我們單獨(dú)創(chuàng)建一個(gè) hash.js 文件放在 public 目錄下,另外在 worker 中也是不允許訪問(wèn) dom 的,但它提供了importScripts`函數(shù)用于導(dǎo)入外部腳本,通過(guò)它導(dǎo)入 spark-md5

// /public/hash.js
self.importScripts("/spark-md5.min.js"); // 導(dǎo)入腳本
// 生成文件 hash
self.onmessage = e => {
  const { fileChunkList } = e.data;
  const spark = new self.SparkMD5.ArrayBuffer();
  let percentage = 0;
  let count = 0;
  const loadNext = index => {
    // 新建讀取器
    const reader = new FileReader();
    // 設(shè)定讀取數(shù)據(jù)格式并開始讀取
    reader.readAsArrayBuffer(fileChunkList[index].file);
    // 監(jiān)聽讀取完成
    reader.onload = e => {
      count++;
      // 獲取讀取結(jié)果并交給spark計(jì)算hash
      spark.append(e.target.result);
      if (count === fileChunkList.length) {
        self.postMessage({
          percentage: 100,
          // 獲取最終hash
          hash: spark.end()
        });
        self.close();
      } else {
        percentage += 100 / fileChunkList.length;
        self.postMessage({
          percentage
        });
        // 遞歸計(jì)算下一個(gè)切片
        loadNext(count);
      }
    };
  };
  loadNext(0);
};

小結(jié)

  • 獲取上傳文件
  • 文件切片后存入數(shù)組 fileChunkList.push({ file: file.slice(cur, cur + size) });
  • 生成文件hash(非必須)
  • 根據(jù)文件切片列表生成請(qǐng)求列表
  • 并發(fā)請(qǐng)求
  • 待全部請(qǐng)求完成后發(fā)送合并請(qǐng)求

文件秒傳

實(shí)際是障眼法,用來(lái)欺騙用戶的。

原理:在文件上傳之前先計(jì)算出文件的hash,然后發(fā)送給后端進(jìn)行驗(yàn)證,看后端是否存在這個(gè)hash,如果存在,則證明這個(gè)文件上傳過(guò),則直接提示用戶秒傳成功

// 切片加工(上傳前預(yù)處理 為文件添加hash等)
async handleUpload() {
  if (!this.container.file) return;
  // 切片生成
  const fileChunkList = this.createFileChunk(this.container.file);
  // hash生成
  this.container.hash = await this.calculateHash(fileChunkList);
    
  // hash驗(yàn)證 (verify為后端驗(yàn)證接口請(qǐng)求)
  const { haveExisetd } = await verify(this.container.hash)
  // 判斷
  if(haveExisetd) {
  	this.$message.success("秒傳:上傳成功") 
    return   
  } 
   
  this.data = fileChunkList.map(({ file },index) => ({
    chunk: file,
    // 這里的hash為文件名 + 切片序號(hào),也可以用md5對(duì)文件進(jìn)行加密獲取唯一hash值來(lái)代替文件名
    hash: this.container.hash + "-" + index
  }));
  await this.uploadChunks();
}

暫停上傳

原理:將所有的切片存在一個(gè)數(shù)組中,每當(dāng)一個(gè)切片上傳完畢,從數(shù)組中移除,這樣就可以實(shí)現(xiàn)用一個(gè)數(shù)組只保存上傳中的文件。此外,因?yàn)橐獣和I蟼鳎孕枰袛嗾?qǐng)求 axios中斷請(qǐng)求可以利用AbortController

中斷請(qǐng)求示例

const controller = new AbortController()
axios({
  signal: controller.signal
}).then(() => {});
// 取消請(qǐng)求
controller.abort()

添加暫停上傳功能

// 上傳切片
async uploadChunks() {
 // 需要把requestList放到全局,因?yàn)橐ㄟ^(guò)操控requestList來(lái)實(shí)現(xiàn)中斷
 this.requestList = this.data
 	// 構(gòu)造formData
   .map(({ chunk,hash }) => {
     const formData = new FormData();
     formData.append("chunk", chunk);
     formData.append("hash", hash);
     formData.append("filename", this.container.file.name);
     return { formData };
   })
 	// 發(fā)送請(qǐng)求 上傳切片
   .map(async ({ formData }, index) =>
     request(formData).then(() => {
       // 將請(qǐng)求成功的請(qǐng)求剝離出requestList
       this.requestList.splice(index, 1)
     })
   );
 await Promise.all(this.requestList); // 等待全部切片上傳完畢
 await merge(this.container.file.name) // 發(fā)送請(qǐng)求合并文件
},
// 暫停上傳   
handlePause() {
	this.requestList.forEach((req) => {
        // 為每個(gè)請(qǐng)求新建一個(gè)AbortController實(shí)例
     	const controller = new AbortController();
        req.signal = controller.signal
        controller.abort()
    })
}

恢復(fù)上傳

原理:上傳切片之前,向后臺(tái)發(fā)送請(qǐng)求,接口將已上傳的切片列表返回,通過(guò)切片hash將后臺(tái)已存在的切片過(guò)濾,只上傳未存在的切片

// 切片加工(上傳前預(yù)處理 為文件添加hash等)
async handleUpload() {
  if (!this.container.file) return;
  // 切片生成
  const fileChunkList = this.createFileChunk(this.container.file);
  // 文件hash生成
  this.container.hash = await this.calculateHash(fileChunkList);
  // hash驗(yàn)證 (verify為后端驗(yàn)證接口請(qǐng)求)
  const { haveExisetd, uploadedList } = await verify(this.container.hash)
  // 判斷
  if(haveExisetd) {
  	this.$message.success("秒傳:上傳成功") 
    return   
  } 
  this.data = fileChunkList.map(({ file },index) => ({
    chunk: file,
    // 注:這個(gè)是切片hash   這里的hash為文件名 + 切片序號(hào),也可以用md5對(duì)文件進(jìn)行加密獲取唯一hash值來(lái)代替文件名
    hash: this.container.hash + "-" + index
  }));
  await this.uploadChunks(uploadedList);
}
// 上傳切片
async uploadChunks(uploadedList = []) {
 // 需要把requestList放到全局,因?yàn)橐ㄟ^(guò)操控requestList來(lái)實(shí)現(xiàn)中斷
 this.requestList = this.data
    // 過(guò)濾出來(lái)未上傳的切片
   .filter(({ hash }) => !uploadedList.includes(hash))
 	// 構(gòu)造formData
   .map(({ chunk,hash }) => {
     const formData = new FormData();
     formData.append("chunk", chunk);
     formData.append("hash", hash);
     formData.append("filename", this.container.file.name);
     return { formData };
   })
 	// 發(fā)送請(qǐng)求 上傳切片
   .map(async ({ formData }, index) =>
     request(formData).then(() => {
       // 將請(qǐng)求成功的請(qǐng)求剝離出requestList
       this.requestList.splice(index, 1)
     })
   );
 await Promise.all(this.requestList); // 等待全部切片上傳完畢
 // 合并之前添加一層驗(yàn)證 驗(yàn)證全部切片傳送完畢
 if(uploadedList.length + this.requestList.length == this.data.length){
	await merge(this.container.file.name) // 發(fā)送請(qǐng)求合并文件
 }
},
// 暫停上傳   
handlePause() {
	this.requestList.forEach((req) => {
        // 為每個(gè)請(qǐng)求新建一個(gè)AbortController實(shí)例
     	const controller = new AbortController();
        req.signal = controller.signal
        controller.abort()
    })
}
// 恢復(fù)上傳
async handleRecovery() {
    //獲取已上傳切片列表 (verify為后端驗(yàn)證接口請(qǐng)求)
	const { uploadedList } = await verify(this.container.hash)
    await uploadChunks(uploadedList)
}

添加功能總結(jié)

  • 1.文件秒傳其實(shí)就是一個(gè)簡(jiǎn)單的驗(yàn)證,把文件的hash發(fā)送給后端,后端驗(yàn)證是否存在該文件后將結(jié)果返回,如果存在則提示文件秒傳成功
  • 2.斷點(diǎn)傳送分為兩步,暫停上傳和恢復(fù)上傳。暫停上傳是通過(guò)獲取到未上傳完畢切片列表(完整切片列表剝離請(qǐng)求已完成的切片后形成),對(duì)列表請(qǐng)求進(jìn)行請(qǐng)求中斷實(shí)現(xiàn)的?;謴?fù)上傳實(shí)質(zhì)也是一層驗(yàn)證,在上傳文件之前,將文件的hash發(fā)送給后端,后端返回已經(jīng)上傳完畢的切片列表,然后根據(jù)切片hash將后端返回的切片列表中的切片過(guò)濾出去,只上傳未上傳完成的切片。

到此這篇關(guān)于JavaScript大文件上傳的處理方法之切片上傳的文章就介紹到這了,更多相關(guān)JS切片上傳內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JSON創(chuàng)建鍵值對(duì)(key是中文或者數(shù)字)方式詳解

    JSON創(chuàng)建鍵值對(duì)(key是中文或者數(shù)字)方式詳解

    這篇文章主要介紹了JSON創(chuàng)建鍵值對(duì)(key是中文或者數(shù)字)方式詳解,需要的朋友可以參考下
    2017-08-08
  • 一文了解JavaScript閉包函數(shù)

    一文了解JavaScript閉包函數(shù)

    閉包是js的一個(gè)難點(diǎn)也是它的一個(gè)特色,是我們必須掌握的js高級(jí)特性,下面這篇文章主要給大家介紹了關(guān)于JavaScript閉包函數(shù)的相關(guān)資料,需要的朋友可以參考下
    2021-11-11
  • antd Upload 文件上傳的示例代碼

    antd Upload 文件上傳的示例代碼

    這篇文章主要介紹了antd Upload 文件上傳的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-12-12
  • 分析JS單線程異步io回調(diào)的特性

    分析JS單線程異步io回調(diào)的特性

    這篇文章主要分析了javascript單線程異步io回調(diào)的特性這個(gè)問(wèn)題,希望我們整理的內(nèi)容能幫助到你。
    2017-12-12
  • Javascript腳本獲取form和input內(nèi)容的方法(兩種方法)

    Javascript腳本獲取form和input內(nèi)容的方法(兩種方法)

    隨著js的發(fā)展,許多的網(wǎng)頁(yè)數(shù)據(jù)處理完全可以由js腳本解決,而不需要發(fā)送到服務(wù)器,這里分享兩種Javascript腳本獲取form和input內(nèi)容的方法,感興趣的朋友跟隨小編一起看看吧
    2023-05-05
  • 百度小程序之間的頁(yè)面通信過(guò)程詳解

    百度小程序之間的頁(yè)面通信過(guò)程詳解

    這篇文章主要介紹了百度小程序之間的頁(yè)面通信,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-07-07
  • 詳解uniapp如何處理圖片加載過(guò)程中的錯(cuò)誤

    詳解uniapp如何處理圖片加載過(guò)程中的錯(cuò)誤

    這篇文章給大家詳細(xì)介紹了uniapp如何處理圖片加載過(guò)程中的錯(cuò)誤,文章通過(guò)代碼示例介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2023-10-10
  • js實(shí)現(xiàn)簡(jiǎn)單放大鏡效果

    js實(shí)現(xiàn)簡(jiǎn)單放大鏡效果

    這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)簡(jiǎn)單放大鏡效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • javascript vvorld 在線加密破解方法

    javascript vvorld 在線加密破解方法

    朋友公司開發(fā)的在線JS加密站點(diǎn),內(nèi)測(cè)中,自己試過(guò)不能找到加密后的源代碼,不知道還有那位大大能夠破解
    2008-11-11
  • js如何判斷用戶是否是用微信瀏覽器

    js如何判斷用戶是否是用微信瀏覽器

    微信內(nèi)置瀏覽器屏蔽了下載鏈接,如果用戶是用微信內(nèi)置瀏覽器打開的,則提示用戶換一個(gè)瀏覽器打開頁(yè)面,那么該如何判斷用戶是否是用微信瀏覽器呢
    2014-06-06

最新評(píng)論

景泰县| 睢宁县| 治多县| 清水河县| 澄江县| 法库县| 漳浦县| 潼关县| 涡阳县| 揭阳市| 天全县| 湄潭县| 赤壁市| 蒙城县| 沽源县| 会宁县| 桂阳县| 依兰县| 崇信县| 莆田市| 尉犁县| 海丰县| 琼结县| 青冈县| 台安县| 伊宁市| 徐州市| 顺义区| 姚安县| 潞西市| 温州市| 玛纳斯县| 鄂伦春自治旗| 盐山县| 安岳县| 尚义县| 湖南省| 临猗县| 开平市| 康保县| 鸡东县|