大文件上傳一次性上傳對(duì)于服務(wù)器壓力比較大柠并,所以對(duì)于大文件多用切片上傳
<template>
<el-upload
action
drag
:auto-upload="false"
:show-file-list="false"
:on-change="handleChange"
v-bind="$attrs"
v-on="$listeners"
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">
將文件拖到此處仍侥,或
<em>點(diǎn)擊上傳</em>
</div>
<div class="el-upload__tip" slot="tip">
支持?jǐn)U展名:pdf.doc.pptx.mp4.mp3,視頻大小5G以?xún)?nèi)
<div class="file_info" v-if="percent">
<div class="ellipsis-one">{{ filename }}</div>
<el-progress :percentage="Math.ceil(percent)"></el-progress>
</div>
</div>
</el-upload>
</template>
<script>
import axios from "axios";
import SparkMD5 from "spark-md5";
export default {
inheritAttrs: false, // 不繼承父組件非 props 屬性,如 class style
data() {
return {
action: `${process.env.VUE_APP_API_PATH}/api/services/app/FileUpLoadService/FileUploadSingle`,
percent: 0, // 總進(jìn)度
percentCount: 0, // 進(jìn)度值
isSuspend: true, // 是否暫停
filename: "", // 文件名
};
},
methods: {
// * 處理文件變化
async handleChange(file) {
if (!file) return;
this.$message.info("正在分析文件中...");
this.filename = file.name;
this.percent = 0;
this.percentCount = 0;
// 獲取文件并轉(zhuǎn)成 ArrayBuffer 對(duì)象
const fileObj = file.raw;
let buffer;
try {
buffer = await this.fileToBuffer(fileObj);
} catch (error) {
console.log("?? ~ handleChange ~ error:", error);
}
// 將文件按固定大小(5M)進(jìn)行切片合愈,注意此處同時(shí)聲明了多個(gè)常量
const chunkSize = 5 * 1024 * 1024,
chunkList = [], // 保存所有切片的數(shù)組
chunkListLength = Math.ceil(fileObj.size / chunkSize), // 計(jì)算總共多個(gè)切片
suffix = /\.([0-9A-z]+)$/.exec(fileObj.name)[1]; // 文件后綴名
// 根據(jù)文件內(nèi)容生成 hash 值
const spark = new SparkMD5.ArrayBuffer();
spark.append(buffer);
const hash = spark.end();
// 生成切片,這里后端要求傳遞的參數(shù)為字節(jié)數(shù)據(jù)塊(chunk)和每個(gè)數(shù)據(jù)塊的文件名(fileName)
let startPos = 0; // 切片時(shí)的初始位置
for (let i = 0; i < chunkListLength; i++) {
const item = {
chunk: fileObj.slice(startPos, startPos + chunkSize),
fileName: `${hash}_${i}.${suffix}`, // 文件名規(guī)則按照 hash_1.jpg 命名
};
startPos += chunkSize;
chunkList.push(item);
}
this.chunkList = chunkList; // sendRequest 要用到
this.hash = hash; // sendRequest 要用到
this.sendRequest();
},
// * 發(fā)送切片
async sendChunks(formData, index) {
await axios({
url: "http://127.0.0.1:3000/upload/bigFiles",
method: "post",
headers: { "Content-Type": "multipart/form-data", token: this.headers.token },
data: formData,
});
// 成功
if (this.percentCount === 0) {
// 避免上傳成功后會(huì)刪除切片改變 chunkList 的長(zhǎng)度影響到 percentCount 的值
this.percentCount = 100 / this.chunkList.length;
}
this.percent += this.percentCount; // 改變進(jìn)度
this.chunkList.splice(index, 1); // 一旦上傳成功就刪除這一個(gè) chunk,方便斷點(diǎn)續(xù)傳
},
// * 發(fā)送請(qǐng)求
sendRequest() {
const requestList = []; // 請(qǐng)求集合
this.chunkList.forEach((item, index) => {
const fn = () => {
const formData = new FormData();
formData.set("name", item.fileName);
formData.append("chunks", item.chunk);
return this.sendChunks(formData, index);
};
requestList.push(fn);
});
let i = 0; // 記錄發(fā)送的請(qǐng)求個(gè)數(shù)
// 文件切片全部發(fā)送完畢后适贸,需要請(qǐng)求 '/merge' 接口,把文件的 hash 傳遞給服務(wù)器
const complete = async () => {
const result = await axios({
url: "http://127.0.0.1:3000/upload/merge",
method: "get",
headers: { token: this.headers.token },
params: { hash: this.hash },
});
this.$message.success("上傳成功");
console.log("上傳成功--臨時(shí)訪問(wèn)路徑", result.data.data.url);
};
const send = async () => {
if (!this.isSuspend) return;
if (i >= requestList.length) {
return complete(); // 發(fā)送完畢
}
await requestList[i]();
i++;
send();
};
send(); // 發(fā)送請(qǐng)求
},
// * 按下暫停按鈕
handleClickBtn() {
this.isSuspend = !this.isSuspend;
// 如果不暫停則繼續(xù)上傳
if (this.isSuspend) this.sendRequest();
},
// * 將 File 對(duì)象轉(zhuǎn)為 ArrayBuffer
fileToBuffer(file) {
return new Promise((resolve, reject) => {
const fr = new FileReader();
fr.onload = event => {
resolve(event.target.result);
};
fr.readAsArrayBuffer(file);
fr.onerror = event => {
reject(new Error("轉(zhuǎn)換文件格式發(fā)生錯(cuò)誤-->" + event.target.error));
};
});
},
},
computed: {
headers() {
return {
Authorization: `Bearer ${this.$store.state.user.session.accessToken}`,
// 自己的token 測(cè)試大文件上傳
token:
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuaWNrbmFtZSI6InBhbmdodSIsInBhc3N3b3JkIjoiMTIzNDU2IiwiaWF0IjoxNzA1NzU3Njk3LCJleHAiOjE3MDU4MDA4OTd9.JdNbM7Hed31opf2Ktagudy1FkGRh7jTIcIYedXJ_43U",
};
},
},
};
</script>
<style lang="scss" scoped>
@import "@/styles/index.scss";
.file_info {
margin-top: 7px;
max-width: 360px;
}
.ellipsis-one {
@include ellipsis-one;
}
</style>
最后編輯于 :
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者