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

這種方式支持選擇多個文件,支持拖拽,支持上傳速度和剩余估計時間顯示,上傳速度也挺快,可以滿足我們的需求。
參考文檔:
https://blog.csdn.net/niugang0920/article/details/89387209
https://github.com/simple-uploader/vue-uploader
https://blog.csdn.net/oppo5630/article/details/80880224