大文件上傳如何做斷點(diǎn)續(xù)傳您单?

一斋荞、是什么

不管怎樣簡單的需求,在量級(jí)達(dá)到一定層次時(shí)虐秦,都會(huì)變得異常復(fù)雜

文件上傳簡單平酿,文件變大就復(fù)雜

上傳大文件時(shí),以下幾個(gè)變量會(huì)影響我們的用戶體驗(yàn)

  • 服務(wù)器處理數(shù)據(jù)的能力
  • 請(qǐng)求超時(shí)
  • 網(wǎng)絡(luò)波動(dòng)

上傳時(shí)間會(huì)變長悦陋,高頻次文件上傳失敗蜈彼,失敗后又需要重新上傳等等

為了解決上述問題,我們需要對(duì)大文件上傳單獨(dú)處理

這里涉及到分片上傳及斷點(diǎn)續(xù)傳兩個(gè)概念

分片上傳

分片上傳俺驶,就是將所要上傳的文件幸逆,按照一定的大小,將整個(gè)文件分隔成多個(gè)數(shù)據(jù)塊(Part)來進(jìn)行分片上傳

如下圖

上傳完之后再由服務(wù)端對(duì)所有上傳的文件進(jìn)行匯總整合成原始的文件

大致流程如下:

  1. 將需要上傳的文件按照一定的分割規(guī)則,分割成相同大小的數(shù)據(jù)塊还绘;
  2. 初始化一個(gè)分片上傳任務(wù)楚昭,返回本次分片上傳唯一標(biāo)識(shí);
  3. 按照一定的策略(串行或并行)發(fā)送各個(gè)分片數(shù)據(jù)塊拍顷;
  4. 發(fā)送完成后抚太,服務(wù)端根據(jù)判斷數(shù)據(jù)上傳是否完整,如果完整菇怀,則進(jìn)行數(shù)據(jù)塊合成得到原始文件

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

斷點(diǎn)續(xù)傳指的是在下載或上傳時(shí)凭舶,將下載或上傳任務(wù)人為的劃分為幾個(gè)部分

每一個(gè)部分采用一個(gè)線程進(jìn)行上傳或下載,如果碰到網(wǎng)絡(luò)故障爱沟,可以從已經(jīng)上傳或下載的部分開始繼續(xù)上傳下載未完成的部分帅霜,而沒有必要從頭開始上傳下載。用戶可以節(jié)省時(shí)間呼伸,提高速度

一般實(shí)現(xiàn)方式有兩種:

  • 服務(wù)器端返回身冀,告知從哪開始
  • 瀏覽器端自行處理

上傳過程中將文件在服務(wù)器寫為臨時(shí)文件,等全部寫完了(文件上傳完)括享,將此臨時(shí)文件重命名為正式文件即可

如果中途上傳中斷過搂根,下次上傳的時(shí)候根據(jù)當(dāng)前臨時(shí)文件大小,作為在客戶端讀取文件的偏移量铃辖,從此位置繼續(xù)讀取文件數(shù)據(jù)塊剩愧,上傳到服務(wù)器從此偏移量繼續(xù)寫入文件即可

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

整體思路比較簡單娇斩,拿到文件仁卷,保存文件唯一性標(biāo)識(shí),切割文件犬第,分段上傳锦积,每次上傳一段,根據(jù)唯一性標(biāo)識(shí)判斷文件上傳進(jìn)度歉嗓,直到文件的全部片段上傳完畢

下面的內(nèi)容都是偽代碼

讀取文件內(nèi)容:

const input = document.querySelector('input');
input.addEventListener('change', function() {
    var file = this.files[0];
});

可以使用md5實(shí)現(xiàn)文件的唯一性

const md5code = md5(file);

然后開始對(duì)文件進(jìn)行分割

var reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.addEventListener("load", function(e) {
    //每10M切割一段,這里只做一個(gè)切割演示丰介,實(shí)際切割需要循環(huán)切割,
    var slice = e.target.result.slice(0, 10*1024*1024);
});

h5上傳一個(gè)(一片)

const formdata = new FormData();
formdata.append('0', slice);
//這里是有一個(gè)坑的鉴分,部分設(shè)備無法獲取文件名稱哮幢,和文件類型,這個(gè)在最后給出解決方案
formdata.append('filename', file.filename);
var xhr = new XMLHttpRequest();
xhr.addEventListener('load', function() {
    //xhr.responseText
});
xhr.open('POST', '');
xhr.send(formdata);
xhr.addEventListener('progress', updateProgress);
xhr.upload.addEventListener('progress', updateProgress);

function updateProgress(event) {
    if (event.lengthComputable) {
        //進(jìn)度條
    }
}

這里給出常見的圖片和視頻的文件類型判斷

function checkFileType(type, file, back) {
/**
* type png jpg mp4 ...
* file input.change=> this.files[0]
* back callback(boolean)
*/
    var args = arguments;
    if (args.length != 3) {
        back(0);
    }
    var type = args[0]; // type = '(png|jpg)' , 'png'
    var file = args[1];
    var back = typeof args[2] == 'function' ? args[2] : function() {};
    if (file.type == '') {
        // 如果系統(tǒng)無法獲取文件類型志珍,則讀取二進(jìn)制流家浇,對(duì)二進(jìn)制進(jìn)行解析文件類型
        var imgType = [
            'ff d8 ff', //jpg
            '89 50 4e', //png

            '0 0 0 14 66 74 79 70 69 73 6F 6D', //mp4
            '0 0 0 18 66 74 79 70 33 67 70 35', //mp4
            '0 0 0 0 66 74 79 70 33 67 70 35', //mp4
            '0 0 0 0 66 74 79 70 4D 53 4E 56', //mp4
            '0 0 0 0 66 74 79 70 69 73 6F 6D', //mp4

            '0 0 0 18 66 74 79 70 6D 70 34 32', //m4v
            '0 0 0 0 66 74 79 70 6D 70 34 32', //m4v

            '0 0 0 14 66 74 79 70 71 74 20 20', //mov
            '0 0 0 0 66 74 79 70 71 74 20 20', //mov
            '0 0 0 0 6D 6F 6F 76', //mov

            '4F 67 67 53 0 02', //ogg
            '1A 45 DF A3', //ogg

            '52 49 46 46 x x x x 41 56 49 20', //avi (RIFF fileSize fileType LIST)(52 49 46 46,DC 6C 57 09,41 56 49 20,4C 49 53 54)
        ];
        var typeName = [
            'jpg',
            'png',
            'mp4',
            'mp4',
            'mp4',
            'mp4',
            'mp4',
            'm4v',
            'm4v',
            'mov',
            'mov',
            'mov',
            'ogg',
            'ogg',
            'avi',
        ];
        var sliceSize = /png|jpg|jpeg/.test(type) ? 3 : 12;
        var reader = new FileReader();
        reader.readAsArrayBuffer(file);
        reader.addEventListener("load", function(e) {
            var slice = e.target.result.slice(0, sliceSize);
            reader = null;
            if (slice && slice.byteLength == sliceSize) {
                var view = new Uint8Array(slice);
                var arr = [];
                view.forEach(function(v) {
                    arr.push(v.toString(16));
                });
                view = null;
                var idx = arr.join(' ').indexOf(imgType);
                if (idx > -1) {
                    back(typeName[idx]);
                } else {
                    arr = arr.map(function(v) {
                        if (i > 3 && i < 8) {
                            return 'x';
                        }
                        return v;
                    });
                    var idx = arr.join(' ').indexOf(imgType);
                    if (idx > -1) {
                        back(typeName[idx]);
                    } else {
                        back(false);
                    }

                }
            } else {
                back(false);
            }

        });
    } else {
        var type = file.name.match(/\.(\w+)$/)[1];
        back(type);
    }
}

調(diào)用方法如下

checkFileType('(mov|mp4|avi)',file,function(fileType){
    // fileType = mp4,
    // 如果file的類型不在枚舉之列,則返回false
});

上面上傳文件的一步碴裙,可以改成:

formdata.append('filename', md5code+'.'+fileType);

有了切割上傳后钢悲,也就有了文件唯一標(biāo)識(shí)信息点额,斷點(diǎn)續(xù)傳變成了后臺(tái)的一個(gè)小小的邏輯判斷

后端主要做的內(nèi)容為:根據(jù)前端傳給后臺(tái)的md5值,到服務(wù)器磁盤查找是否有之前未完成的文件合并信息(也就是未完成的半成品文件切片)莺琳,取到之后根據(jù)上傳切片的數(shù)量还棱,返回?cái)?shù)據(jù)告訴前端開始從第幾節(jié)上傳

如果想要暫停切片的上傳,可以使用XMLHttpRequestabort方法

三惭等、使用場景

  • 大文件加速上傳:當(dāng)文件大小超過預(yù)期大小時(shí)珍手,使用分片上傳可實(shí)現(xiàn)并行上傳多個(gè) Part, 以加快上傳速度
  • 網(wǎng)絡(luò)環(huán)境較差:建議使用分片上傳辞做。當(dāng)出現(xiàn)上傳失敗的時(shí)候琳要,僅需重傳失敗的Part
  • 流式上傳:可以在需要上傳的文件大小還不確定的情況下開始上傳。這種場景在視頻監(jiān)控等行業(yè)應(yīng)用中比較常見

小結(jié)

當(dāng)前的偽代碼秤茅,只是提供一個(gè)簡單的思路稚补,想要把事情做到極致,我們還需要考慮到更多場景框喳,比如

  • 切片上傳失敗怎么辦
  • 上傳過程中刷新頁面怎么辦
  • 如何進(jìn)行并行上傳
  • 切片什么時(shí)候按數(shù)量切课幕,什么時(shí)候按大小切
  • 如何結(jié)合 Web Work 處理大文件上傳
  • 如何實(shí)現(xiàn)秒傳

人生又何嘗不是如此,極致的人生體驗(yàn)有無限可能五垮,越是后面才發(fā)現(xiàn)越是精彩 _

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末乍惊,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子放仗,更是在濱河造成了極大的恐慌润绎,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诞挨,死亡現(xiàn)場離奇詭異凡橱,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)亭姥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來顾稀,“玉大人达罗,你說我怎么就攤上這事【哺眩” “怎么了粮揉?”我有些...
    開封第一講書人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長抚笔。 經(jīng)常有香客問我扶认,道長,這世上最難降的妖魔是什么殊橙? 我笑而不...
    開封第一講書人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任辐宾,我火速辦了婚禮狱从,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘叠纹。我一直安慰自己季研,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開白布誉察。 她就那樣靜靜地躺著与涡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪驼卖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,829評(píng)論 1 290
  • 那天鸿秆,我揣著相機(jī)與錄音,去河邊找鬼谬莹。 笑死檩奠,一個(gè)胖子當(dāng)著我的面吹牛附帽,可吹牛的內(nèi)容都是我干的埠戳。 我是一名探鬼主播蕉扮,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼喳钟!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起奔则,我...
    開封第一講書人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤蛮寂,失蹤者是張志新(化名)和其女友劉穎易茬,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抽莱,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡范抓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了食铐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片匕垫。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖虐呻,靈堂內(nèi)的尸體忽然破棺而出象泵,到底是詐尸還是另有隱情寞秃,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布单芜,位于F島的核電站蜕该,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏洲鸠。R本人自食惡果不足惜堂淡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望扒腕。 院中可真熱鬧绢淀,春花似錦、人聲如沸瘾腰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蹋盆。三九已至费薄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間栖雾,已是汗流浹背楞抡。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留析藕,地道東北人召廷。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像账胧,于是被迫代替她去往敵國和親竞慢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • 前言 文件上傳是一個(gè)老生常談的話題了败潦,在文件相對(duì)比較小的情況下,可以直接把文件轉(zhuǎn)化為字節(jié)流上傳到服務(wù)器吮播,但在文件比...
    碼農(nóng)突圍閱讀 748評(píng)論 0 2
  • 16宿命:用概率思維提高你的勝算 以前的我是風(fēng)險(xiǎn)厭惡者,不喜歡去冒險(xiǎn)眼俊,但是人生放棄了冒險(xiǎn)意狠,也就放棄了無數(shù)的可能。 ...
    yichen大刀閱讀 6,041評(píng)論 0 4
  • 公元:2019年11月28日19時(shí)42分農(nóng)歷:二零一九年 十一月 初三日 戌時(shí)干支:己亥乙亥己巳甲戌當(dāng)月節(jié)氣:立冬...
    石放閱讀 6,876評(píng)論 0 2