Vue+Springboot實現(xiàn)大文件上傳的二種方式

vue+ElementUI實現(xiàn)的web管理,后臺用springboot來實現(xiàn)的,需要實現(xiàn)上傳幾百M到幾個G的文件上傳并顯示進(jìn)度條。
嘗試了幾種方式,以下是一些總結(jié)

1. 利用ElementUI的el-upload

沒有用el-upload的缺省上傳,覆蓋默認(rèn)的上傳行為,自定義上傳的實現(xiàn)。

<el-upload action=""
            :http-request="uploadSectionFile">
    <el-button size="small" type="primary">點擊上傳</el-button>
 </el-upload>
    <el-progress v-show="showProgress" :text-inside="true" :stroke-width="18"
                       :percentage="uploadPercent"></el-progress>

http-request 賦值就可以自定義一個上傳函數(shù)。然后使用 el-progress 來顯示進(jìn)度。

uploadSectionFile (param) {
      let form = new FormData()
      var that = this
      form.append('file', param.file)
      form.append('dir', 'temp1')
      that.$axios.post('http://192.168.1.65/upload', form, {
        headers: {
          'Content-Type': 'multipart/form-data'
        },
        onUploadProgress: progressEvent => {
          that.uploadPercent = (progressEvent.loaded / progressEvent.total * 100) | 0
        }
      }).then((res) => {
        console.log('上傳結(jié)束')
        console.log(res)
      }).catch((err) => {
        console.log('上傳錯誤')
        console.log(err)
      })
    },

進(jìn)度條就是用 axios 帶的 onUploadProgress 事件來實現(xiàn)獲取和顯示進(jìn)度

后端對應(yīng)java實現(xiàn)一個基本的表單上傳接口

@RequestMapping(value = "/upload", method = {RequestMethod.POST})
    public Result upload(@RequestParam(required = false) String dir, @RequestParam(required = false) MultipartFile file) {
        try {
            System.out.println("upload start = " + System.currentTimeMillis());
            String videoUrl = uploadFile(file, dir);
            System.out.println("upload end = " + System.currentTimeMillis());
            return ResultUtil.success("上傳成功", videoUrl);
        } catch (Exception var3) {
            return ResultUtil.fail(var3.getMessage());
        }
    }

public String uploadFile(MultipartFile file, String resSort) throws Exception {
        String shortPath =  file.getOriginalFilename();
        File dest = new File("C://Temp", shortPath);
        if (!dest.getParentFile().exists()) {
            boolean rel = dest.getParentFile().mkdirs();
            if (!rel) {
                throw new Exception("文件夾創(chuàng)建失敗");
            }
        }
        InputStream is = file.getInputStream();
        OutputStream os = new FileOutputStream(dest);
        try {
            byte[] buffer = new byte[8 * 1024];
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
        } catch (Exception e) {
            throw e;
        } finally {
            if (is != null) {
                is.close();
            }
            if (os != null) {
                os.close();
            }
        }
        return shortPath;
    }

功能實現(xiàn)沒有問題。但是有2個問題:

  • 上傳速度太慢,沒有分片單線程上傳1個G的文件即使在局域網(wǎng)也很慢
  • 上傳顯示的進(jìn)度條不準(zhǔn)確,進(jìn)度已經(jīng)100%了,但是還需要等很久在服務(wù)端才生成完文件

其中第二個問題做了一些簡單的研究,上傳的大概流程分三步驟:

  • 是前端先和服務(wù)端數(shù)據(jù)交互,服務(wù)端的內(nèi)存里先保存所有上傳的數(shù)據(jù)內(nèi)容,這個階段結(jié)束后,進(jìn)度就已經(jīng)是100%了
  • 服務(wù)端內(nèi)存會把內(nèi)存數(shù)據(jù)保存到本地一個臨時目錄,我們可以在 application.properties 文件里設(shè)置這個目錄的值 spring.servlet.multipart.location
spring.servlet.multipart.location= C://Temp1 
# 最大支持文件大小
spring.servlet.multipart.max-file-size=1000MB
# 最大支持請求大小
spring.servlet.multipart.max-request-size=1000MB
  • 臨時目錄的文件拷貝到最終目錄,然后刪除臨時目錄的文件

以上原因,所以考慮還是用分片上傳。

2. 利用百度的webuploader

WebUploader是網(wǎng)上比較推薦的方式,分片上傳大文件速度很快,但是我在 vue 下并沒有嘗試成功,最后也放棄了。它也有幾個問題就是:

  • 必須依賴 jquery
  • 不能 import 導(dǎo)入,只能在 index.html 里包含
  • 不管是顯式的還是自動的,相關(guān)的回調(diào)總是無法觸發(fā)(筆者前端水平有限)

3. 利用vue-uploader

vue-uploader 是基于vue的uploader組件,缺省就是分片上傳。

通過npm安裝,基本流程參考github上的說明即可。
上傳的基本原理就是前端根據(jù)文件大小,按塊大小分成很多塊,然后多線程同時上傳多個塊,同時調(diào)用服務(wù)端的上傳接口,服務(wù)端會生成很多小塊小塊的文件。
所有塊都上傳完之后,前端再調(diào)用一個服務(wù)端的merge接口,服務(wù)端把前面收到的所有塊文件按順序組合成最終的文件。

<uploader :options="options" class="uploader-example" 
           @file-complete="fileComplete" @complete="complete">
       <uploader-unsupport></uploader-unsupport>
       <uploader-drop>
              <p>Drop files here to upload or</p>
       <uploader-btn>select files</uploader-btn>
       </uploader-drop>
       <uploader-list></uploader-list>
 </uploader>
data () {
    return {
      options: {
        // https://github.com/simple-uploader/Uploader/tree/develop/samples/Node.js
        target: 'http://192.168.1.84:8089/upload',
        testChunks: false, //上傳前判斷塊是否已經(jīng)存在
        simultaneousUploads: 5, //并發(fā)數(shù)
        chunkSize: 8 * 1024 * 1024 //塊大小
      },
...
methods: {
    complete () {
      console.log('complete', arguments)
    },
  fileComplete () {
      console.log('file complete', arguments)
      const file = arguments[0].file
      let url = 'http://192.168.1.84:8089/upload/merge?filename=' + file.name + '&guid=' + arguments[0].uniqueIdentifier
      this.$axios.get(url).then(function (response) {
        console.log(response)
      }).catch(function (error) {
        console.log(error)
      })
    }

對應(yīng)的服務(wù)端需要實現(xiàn)2個接口,upload 和 merge ,具體可以參考git

image.png

這種方式支持選擇多個文件,支持拖拽,支持上傳速度和剩余估計時間顯示,上傳速度也挺快,可以滿足我們的需求。

參考文檔:
https://blog.csdn.net/niugang0920/article/details/89387209
https://github.com/simple-uploader/vue-uploader
https://blog.csdn.net/oppo5630/article/details/80880224

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 大文件的上傳是我一直以來想學(xué)習(xí)的一個技術(shù)點,今天在項目閑暇之時,終于有機(jī)會自己嘗試了一把,本文僅僅是個Demo,各...
    小久代碼搬運閱讀 2,109評論 0 1
  • 基于Vue的一些資料 內(nèi)容 UI組件 開發(fā)框架 實用庫 服務(wù)端 輔助工具 應(yīng)用實例 Demo示例 element★...
    嘗了又嘗閱讀 1,282評論 0 1
  • UI組件 element- 餓了么出品的Vue2的web UI工具套件 Vux- 基于Vue和WeUI的組件庫 m...
    小姜先森o0O閱讀 10,110評論 0 72
  • 簡說Vue (組件庫) https://github.com/ElemeFE/element" 餓了么出品的VUE...
    Estrus丶閱讀 1,908評論 0 1
  • UI組件 element- 餓了么出品的Vue2的web UI工具套件 Vux- 基于Vue和WeUI的組件庫 m...
    柴東啊閱讀 15,960評論 2 140

友情鏈接更多精彩內(nèi)容