vue+大文件斷點續傳


  

根據部門的業務需求,需要在網絡狀態不良的情況下上傳很大的文件(1G+)。
其中會遇到的問題:
1,文件過大,超出服務端的請求大小限制;
2,請求時間過長,請求超時;
3,傳輸中斷,必須重新上傳導致前功盡棄。
解決方案實現思路,拿到文件,保存文件唯一性標識,切割文件、分片上傳、文件MD5驗證、斷點續傳、手動重試上傳。


鑒於過往有使用過webupload文件上傳組件的經驗,於是此次采用的是Plupload作為替換。Plupload是一款由著名的web編輯器TinyMCE團隊開發的上傳組件,簡單易用且功能強大。

Plupload有以下功能和特點

  1. 擁有多種上傳方式:HTML5、flash、silverlight以及傳統的<input type=”file” />。Plupload會自動偵測當前的環境,選擇最合適的上傳方式,並且會優先使用HTML5的方式。所以你完全不用去操心當前的瀏覽器支持哪些上傳方式,Plupload會自動為你選擇最合適的方式。
  2. 支持以拖拽的方式來選取要上傳的文件
  3. 支持在前端壓縮圖片,即在圖片文件還未上傳之前就對它進行壓縮
  4. 可以直接讀取原生的文件數據,這樣的好處就是例如可以在圖片文件還未上傳之前就能把它顯示在頁面上預覽
  5. 支持把大文件切割成小片進行上傳,因為有些瀏覽器對很大的文件比如幾G的一些文件無法上傳。

環境

  • vue2.x
  • webpack3.x
  • axios

代碼

npm安裝plupload,文件引入組件,

1
2
3
4
5
6
7
8
9
10
11
12
<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(),確保文件在中斷后繼續上傳的准確性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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'

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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加密並上傳校驗,這里有寫進度條相關的控制,不一一展示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
         }
       })
     }

  

詳細的配置信息可以參考我寫的這篇文章:http://blog.ncmem.com/wordpress/2019/08/09/vue%e5%a4%a7%e6%96%87%e4%bb%b6%e6%96%ad%e7%82%b9%e7%bb%ad%e4%bc%a0/ 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM