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

一文帶你徹底搞懂JS大文件分片上傳的實(shí)現(xiàn)

 更新時(shí)間:2023年05月31日 10:44:54   作者:落葉飄  
這篇文章主要為大家詳細(xì)介紹了前端JavaScript是如何實(shí)現(xiàn)大文件分片上傳的,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,需要的可以參考一下

學(xué)習(xí)AbortController

AbortController 接口表示一個(gè)控制器對(duì)象,允許你根據(jù)需要中止一個(gè)或多個(gè) Web 請(qǐng)求。

可以通過這個(gè)來實(shí)現(xiàn)取消或者中斷請(qǐng)求的功能。

axios.abort();底層實(shí)現(xiàn)就是這個(gè)。

認(rèn)識(shí)了

fse = require('fs-extra')

概述

實(shí)現(xiàn)文件分片上傳的原理就是通過將文件的ArrayBlob形式,通過file(blob)的方法slice,來實(shí)現(xiàn)將文件拆成幾個(gè)部分,然后排上順序傳到服務(wù)端,最后傳完了調(diào)用服務(wù)端的merge合并接口,服務(wù)端將文件合并。

服務(wù)端實(shí)現(xiàn)原理,上傳前創(chuàng)建文件夾,命名注意了,可以參考給到的代碼。然后將獲得的文件通過fse的寫入方法,寫入到我們創(chuàng)建的文件夾中,并且有相應(yīng)的排序。最后我們通過合并方法將文件合并到一個(gè)文件。

詳細(xì)學(xué)習(xí)

Client端

getChunkListAndFileMd5函數(shù)

這個(gè)函數(shù)用來創(chuàng)建分片數(shù)組,以及生成Hash簽名。

創(chuàng)建分片列表數(shù)組,我們使用的方法是file.prototype.slice當(dāng)然這里我們?yōu)榱私鉀Q兼容性問題,我們使用的代碼是:

export function getBlobSlice() {
    return (File.prototype.slice ||
        File.prototype.mozSlice ||
        File.prototype.webkitSlice);
}

創(chuàng)建分片:getBlobSlice.call(file,start,end)。這里start、end分別是起始位置和終點(diǎn)位置,我們確認(rèn)好分片大小就可以計(jì)算start和end。

好,那么我們開始了,我們定義size是 5M 代碼如下

const DEFAULT_CHUNK_SIZE = 5 * 1024 * 1024;

我們定義一個(gè)對(duì)象保存默認(rèn)配置:

const DEFAULT_OPTIONS = {
    chunkSize: DEFAULT_CHUNK_SIZE,
};

我們來定義分片的編號(hào),初始值肯定是 0。我們定義chunkSize來保存我們上邊定義的常量分片大小。

let currentChunk = 0;
const chunkSize = this.fileUploaderClientOptions.chunkSize; 

接下來我們需要計(jì)算需要多少個(gè)分片,很簡(jiǎn)單:文件總的大小/每個(gè)分片的大小,最終結(jié)果可能是個(gè)小數(shù),但是為了保證分片的完整肯定是向上取整。

const chunks = Math.ceil(file.size / chunkSize);

定義一個(gè)函數(shù)來加載分片,代碼如下:

function loadNextChunk() {
    const start = currentChunk * chunkSize;
    const end = start + chunkSize >= file.size ? file.size : start + chunkSize;
    const chunk = blobSlice.call(file, start, end);
    chunkList.push(chunk);
    fileReader.readAsArrayBuffer(chunk);
}

拆解這個(gè)函數(shù):

1.計(jì)算start位置,這個(gè)不難理解。

2.計(jì)算end位置,這里需要判斷一下,主要是針對(duì)兩種情況:

文件尺寸很小,小于分片大小,直接取文件的尺寸。

最后一個(gè)切片,大小可能沒有切片尺寸大,我們直接取文件大小的位置。

3.調(diào)用我們上邊說的函數(shù)來實(shí)現(xiàn)分片,并且獲取當(dāng)前的分片。

4.將當(dāng)前的分片push到我們的數(shù)組中。

5.調(diào)用fileReader.readAsArrayBuffer方法來讀取分片。(其實(shí)當(dāng)前的分片是一個(gè)blob實(shí)例,我們通過這個(gè)方法可以讀取到里邊的內(nèi)容)。
接下來我們來看看fileReader是怎么回事。

認(rèn)識(shí)FileReader

不懂fileReader的可以先看看文檔,了解下里邊的方法。

FileReader 對(duì)象允許 Web 應(yīng)用程序異步讀取存儲(chǔ)在用戶計(jì)算機(jī)上的文件(或原始數(shù)據(jù)緩沖區(qū))的內(nèi)容,使用 File 或 Blob 對(duì)象指定要讀取的文件或數(shù)據(jù)。
其中 File 對(duì)象可以是

  • 來自用戶在一個(gè) 元素上選擇文件后返回的FileList對(duì)象
  • 也可以來自拖放操作生成的 DataTransfer對(duì)象,
  • 還可以是來自在一個(gè)HTMLCanvasElement上執(zhí)行mozGetAsFile()方法后返回結(jié)果。

FileReader可以在Web Worker中使用。 (重要,可以很大成都解決性能問題)

這里因?yàn)橛玫搅薋ileReader的方法,所以重點(diǎn)講講這個(gè)對(duì)象的方法。

  • FileReader.abort(); 中止讀取操作。
  • FileReader.readAsArrayBuffer(); 開始讀取指定的 Blob中的內(nèi)容,一旦完成,result 屬性中保存的將是被讀取文件的 ArrayBuffer數(shù)據(jù)對(duì)象。
  • FileReader.readAsDataURL(); 開始讀取指定的Blob中的內(nèi)容。一旦完成,result屬性中將包含一個(gè)data: URL 格式的 Base64 字符串以表示所讀取文件的內(nèi)容。
  • FileReader.readAsText();開始讀取指定的Blob中的內(nèi)容。一旦完成,result屬性中將包含一個(gè)字符串以表示所讀取的文件內(nèi)容。

我們目前需要讀取分片的內(nèi)容,并且需要ArrayBuffer的格式,所以我們創(chuàng)建一個(gè)FileReader對(duì)象實(shí)例,并且使用readAsArrayBuffer方法來讀取文件,通過onload事件來監(jiān)聽,獲取最終結(jié)果。代碼如下:

const fileReader = new FileReader();
fileReader.onload = function (e) {
    // 我們讀取到的結(jié)果是 e.target.result
}
fileReader.onerror = function (e) {
    // 這里表示讀取失敗
}

上邊loadNextChunk函數(shù)中調(diào)用了這個(gè)方法。

fileReader.readAsArrayBuffer(chunk);

使用md5來實(shí)現(xiàn)簽名

用到了 spark-md5 這個(gè)三方庫(kù)。主要使用的方法是生成md5的編碼。

npm install --save spark-md5

npmjs中的spark-md5

github

這個(gè)就是一個(gè)處理分片的一個(gè)庫(kù)。給出了方法:

const spark = new SparkMD5.ArrayBuffer();

將分片 v 添加到對(duì)象中

spark.append(v)

最終結(jié)果返回

const result = spark.end()

uploadFile函數(shù)

這個(gè)函數(shù)用來上傳文件的函數(shù)。

首先我們要獲取上邊我們函數(shù)得到的值 md5、chunkList。

const { md5, chunkList } = yield this.getChunkListAndFileMd5(file);

上傳文件前,需要調(diào)用接口來初始化文件分片上傳,這里我們其實(shí)就是調(diào)用初始化文件分片上傳的接口requestOptions.initFilePartUploadFunc。

yield requestOptions.initFilePartUploadFunc();

這個(gè)接口在后端的實(shí)現(xiàn)其實(shí)就是創(chuàng)建好一個(gè)文件夾用來保存分片,這里就不多說了,之后在學(xué)習(xí)Server代碼的時(shí)候我們細(xì)講。
接下來就是開始上傳我們的分片了,上傳方法requestOptions.uploadPartFileFunc:

for (let index = 0; index < chunkList.length; index++) {
    try {
        yield requestOptions.uploadPartFileFunc(chunkList[index], index);
    }
    catch (e) {
        console.warn(`${index} part upload failed`);
        retryList.push(index);
    }
}

注意了:我們不能保證全部順利上傳,如果中間出現(xiàn)問題中斷了上傳等問題,我們?nèi)绾翁幚恚?/p>

這里我們使用retryTimes來獲取需要重新上傳的列表。

for (let retry = 0; retry < requestOptions.retryTimes; retry++) {
    if (retryList.length > 0) {
        console.log(`retry start, times: ${retry}`);
        for (let a = 0; a < retryList.length; a++) {
            const blobIndex = retryList[a];
            try {
                yield requestOptions.uploadPartFileFunc(chunkList[blobIndex], blobIndex);
                retryList.splice(a, 1);
            }
            catch (e) {
                console.warn(`${blobIndex} part retry upload failed, times: ${retry}`);
            }
        }
    }
}

最后我們調(diào)用上傳結(jié)束的接口requestOptions.finishFilePartUploadFunc(md5),其實(shí)這個(gè)接口主要是通知服務(wù)端,分片都上傳完了,服務(wù)端可以進(jìn)行文件合并了,最終將分片合并成一個(gè)文件。

if (retryList.length === 0) {
    return yield requestOptions.finishFilePartUploadFunc(md5);
}
else {
    throw Error(`upload failed, some chunks upload failed: ${JSON.stringify(retryList)}`);
}

至此,客戶端的操作完畢!

完整代碼

以上就是一文帶你徹底搞懂JS大文件分片上傳的實(shí)現(xiàn)的詳細(xì)內(nèi)容,更多關(guān)于JS大文件分片上傳的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 關(guān)于js中removeEventListener取消事件監(jiān)聽的坑

    關(guān)于js中removeEventListener取消事件監(jiān)聽的坑

    許多入前端不久的人都會(huì)遇到removeEventListener無法清除監(jiān)聽的情況,下面這篇文章主要給大家介紹了關(guān)于js中removeEventListener取消事件監(jiān)聽的坑,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-09-09
  • 重學(xué) JS:為啥 await 不能用在 forEach 中詳解

    重學(xué) JS:為啥 await 不能用在 forEach 中詳解

    這篇文章主要介紹了重學(xué) JS:為啥 await 不能用在 forEach 中,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • 淺談webpack 自動(dòng)刷新與解析

    淺談webpack 自動(dòng)刷新與解析

    這篇文章主要介紹了淺談webpack 自動(dòng)刷新與解析,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-04-04
  • 解決微信授權(quán)成功后點(diǎn)擊按返回鍵出現(xiàn)空白頁(yè)和報(bào)錯(cuò)的問題

    解決微信授權(quán)成功后點(diǎn)擊按返回鍵出現(xiàn)空白頁(yè)和報(bào)錯(cuò)的問題

    這篇文章主要介紹了解決微信授權(quán)成功后點(diǎn)擊按返回鍵出現(xiàn)空白頁(yè)和報(bào)錯(cuò)的問題,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • 深入淺析Node環(huán)境和瀏覽器的區(qū)別

    深入淺析Node環(huán)境和瀏覽器的區(qū)別

    最近有朋友問我node環(huán)境和瀏覽器的區(qū)別這一問題,今天小編就抽空給大家介紹下Node環(huán)境和瀏覽器的區(qū)別,感興趣的朋友一起看看吧
    2018-08-08
  • DOM操作原生js 的bug,使用jQuery 可以消除的解決方法

    DOM操作原生js 的bug,使用jQuery 可以消除的解決方法

    下面小編就為大家?guī)硪黄狣OM操作原生js 的bug,使用jQuery 可以消除的解決方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-09-09
  • jquery實(shí)現(xiàn)的圖片點(diǎn)擊滾動(dòng)效果

    jquery實(shí)現(xiàn)的圖片點(diǎn)擊滾動(dòng)效果

    這篇文章主要介紹了jquery實(shí)現(xiàn)的圖片點(diǎn)擊滾動(dòng)效果,需要的朋友可以參考下
    2014-04-04
  • 最新評(píng)論