背景
根據部門的業務需求,需要在網絡狀態不良的情況下上傳很大的文件(1G+)。
其中會遇到的問題:
1,文件過大,超出服務端的請求大小限制;
2,請求時間過長,請求超時;
3,傳輸中斷,必須重新上傳導致前功盡棄。
解決方案實現思路,拿到文件,保存文件唯一性標識,切割文件、分片上傳、文件MD5驗證、斷點續傳、手動重試上傳。
前言
鑒於過往有使用過webupload文件上傳組件的經驗,於是此次采用的是Plupload作為替換。Plupload是一款由著名的web編輯器TinyMCE團隊開發的上傳組件,簡單易用且功能強大。
Plupload有以下功能和特點
- 擁有多種上傳方式:HTML5、flash、silverlight以及傳統的
<input type=”file” />
。Plupload會自動偵測當前的環境,選擇最合適的上傳方式,並且會優先使用HTML5的方式。所以你完全不用去操心當前的瀏覽器支持哪些上傳方式,Plupload會自動為你選擇最合適的方式。 - 支持以拖拽的方式來選取要上傳的文件
- 支持在前端壓縮圖片,即在圖片文件還未上傳之前就對它進行壓縮
- 可以直接讀取原生的文件數據,這樣的好處就是例如可以在圖片文件還未上傳之前就能把它顯示在頁面上預覽
- 支持把大文件切割成小片進行上傳,因為有些瀏覽器對很大的文件比如幾G的一些文件無法上傳。
環境
- vue2.x
- webpack3.x
- axios
代碼
npm安裝plupload,文件引入組件,
<uploader browse_button="upload_area" :max_retries="3" :url="action" :headers="headers" chunk_size="10MB" drop_element="upload_area" @disableBrowse="!loading" :BeforeUpload="beforeUpload" :ChunkUploaded="chunkUploaded" :FilesAdded="filesAdded" :StateChanged="stateChanged" @inputUploader="inputUploader" />
初始化方法filesAdded(),每次上傳前清空隊列的其他文件,保證上傳的一致性。其次對文件類型進行判斷過濾fileType(),文件進入時進行總md5一次fileMd5(),然后進入文件分片chunkCheckStatus(),每個分片都要進行md5並與后台進行校驗fileMd5(),確保文件在中斷后繼續上傳的准確性。
filesAdded (up, files) { // 刪除上傳隊列中其他文件,只保留最近上傳的文件 let fileLen = files.length, that = this if (fileLen > 1) { files = files.splice(0, fileLen - 1)// 清空上傳隊列 } files.forEach((f) => { f.status = -1 that.dataForm.file = f that.fileType(f.getNative()) if (that.loading) { that.computeStatus = true that.progress = 0 // 文件分片 let chunkSize = 2097152, // Read in chunks of 2MB chunks = Math.ceil(f.size / chunkSize) that.fileMd5(f.getNative(), (e, md5) => { that.dataForm.md5 = md5 if (that.loading == true) { that.count = 0 that.chunkCheckStatus(md5, that.dataForm.fileName, (uploader, dataList) => { that.uploading = uploader if (that.uploading == true) { for (let chunk = 1; chunk <= chunks; chunk++) { that.fileChunkFile(f.getNative(), chunk, (e, chunkFile) => { that.fileMd5(chunkFile, (e, blockMd5) => { that.PostFile(up, chunkFile, chunk, chunks, md5, blockMd5) }) }) } } else { // 去重 that.progress = 0 for (let chunk = 1; chunk <= chunks; chunk++) { let status = 0 dataList.some((item) => { if (item.chunk == chunk) { status = 1 return false } }) if (status == 0) { that.fileChunkFile(f.getNative(), chunk, (e, chunkFile) => { that.fileMd5(chunkFile, (e, blockMd5) => { that.PostFile(up, chunkFile, chunk, chunks, md5, blockMd5) }) }) } } } }) } }) } }) }
文件md5方法,這里使用了SparkMD5,import SparkMD5 from 'spark-md5'
fileMd5 (file, callback) { let that = this var blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice, file = file, chunkSize = 2097152, // Read in chunks of 2MB chunks = Math.ceil(file.size / chunkSize), currentChunk = 0, spark = new SparkMD5.ArrayBuffer(), fileReader = new FileReader() fileReader.onload = function (e) { console.log('read chunk nr', currentChunk + 1, 'of', chunks) spark.append(e.target.result) // Append array buffer currentChunk++ if (currentChunk < chunks) { loadNext() } else { let blockMd5 = '' blockMd5 = spark.end() callback(null, blockMd5) } } fileReader.onerror = function () { callback('oops, something went wrong.') } function loadNext () { var start = currentChunk * chunkSize, end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize fileReader.readAsArrayBuffer(blobSlice.call(file, start, end)) } loadNext() }
文件分片上傳方法,驗證總分片信息后,把每個分片進行md5加密並上傳校驗,這里有寫進度條相關的控制,不一一展示
chunkCheckStatus (md5, fileName, callback) { this.$http({ url: this.$http.adornUrl('/biz/upload/getFileBlockStatus'), method: 'get', params: this.$http.adornParams({ md5: md5, fileName: fileName }) }).then(({ data }) => { if (data && data.code === 0) { if (data.list != null) { this.uploading = false this.chunkCheckData = [] data.list.map((item, index) => { if (item.isUpload == true) { this.count++ this.chunkCheckData.push(item) } }) callback(this.uploading, this.chunkCheckData) return } this.uploading = true callback(this.uploading) } else { this.$message.error(data.msg) this.loading = false this.computeStatus = false return false } }) }
總結
上圖可以清晰的說明文件加密上傳的傳輸流程,在文件上傳加密及Content-Type格式需要與后台協商一致,通常有base64、multipart/form-data兩種類型,要清晰了解分片算法及md5,自定義上傳文件異步代碼,除此之外,可拖拽上傳文件、進度條控制等可更好的豐富文件上傳體驗。
注意
功能在某些舊版瀏覽器中不起作用。