前提条件,后端接口要支持
1.大体思路
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/1faa595353dc4801af180966c1a6fec1.png#pic_center)
2.功能代码(含多文件上传,上传进度,删除功能)
<template>
<div class="upload-file">
<el-upload
multiple
class="upload-demo"
:limit="limit"
:auto-upload="true"
:action="action"
:headers="headers"
:file-list="fileList"
:on-exceed="onExceed"
:on-error="onError"
:on-success="onSuccess"
:before-upload="beforeUpload"
:before-remove="beforeRemove"
:show-file-list="false"
>
<el-button size="mini" type="primary">{{
$t("dataInfo.selectFile")
}}</el-button>
</el-upload>
<el-table
:data="beforeUploadingDisplayData"
stripe
style="width: 100%"
:max-height="350"
>
<el-table-column
align="center"
prop="fileName"
:label="$t('dataInfo.fileName')"
>
</el-table-column>
<el-table-column
align="center"
prop="name"
:label="$t('dataInfo.fileSize')"
width="120"
>
<template slot-scope="scope">
<span>
{{ getUnitSize(scope.row.fileSize) }}
</span>
</template>
</el-table-column>
<el-table-column
align="center"
prop="address"
width="230"
:label="$t('upload.uploadProgress')"
>
<template slot-scope="scope">
<div class="upload-progress" v-if="fileUid[scope.row.uid]">
<el-progress
style="width: 150px"
:percentage="fileUid[scope.row.uid].totalProgress"
></el-progress>
<span>{{
promptStatement(
fileUid[scope.row.uid].totalProgress
)
}}</span>
</div>
<div class="upload-progress" v-else>
<el-progress
style="width: 150px"
:percentage="scope.row.totalProgress"
></el-progress>
<span>{{
promptStatement(scope.row.totalProgress)
}}</span>
</div>
</template>
</el-table-column>
<el-table-column
align="center"
:label="$t('public.operation')"
width="80"
>
<template slot-scope="scope">
<el-button
type="text"
@click="handleDelete(scope.$index)"
icon="el-icon-delete"
style="color: #f56c6c"
></el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { getToken } from "@/utils/auth";
import { checkMd5, uploadChunk, mergeChunks } from "@/api/dataManage/dataInfo";
import SparkMD5 from "spark-md5";
export default {
name: "FileUpload",
props: {
limit: {
type: Number,
default: 5,
},
fileSize: {
type: Number,
default: 5,
},
fileType: {
type: Array,
default: () => ["doc", "xls", "ppt", "txt", "pdf"],
},
},
data() {
return {
number: 0,
uploadList: [],
action: process.env.VUE_APP_BASE_API + "/minio/upload",
headers: {
Authorization: "Bearer " + getToken(),
},
getToken: getToken(),
fileList: [],
chunkSize: 1024 * 1024 * 50,
fileUid: {},
beforeUploadingDisplayData: [],
};
},
methods: {
getUnitSize(value) {
if (value === null || value === undefined) {
return "";
}
const units = ["B", "KB", "MB", "GB", "TB", "PB"];
let index = 0;
while (value >= 1024 && index < units.length - 1) {
value /= 1024;
index++;
}
return `${value.toFixed(2)} ${units[index]}`;
},
beforeUpload(file) {
var beforeUploadFile = {
uid: file.uid,
fileName: file.name,
fileSize: file.size,
totalProgress: 0,
};
this.beforeUploadingDisplayData.push(beforeUploadFile);
if (file.size < this.chunkSize) {
this.number++;
return true;
} else {
this.uploadFile(file);
return false;
}
},
onExceed() {
this.$modal.msgError(
this.$t("upload.fileNumber", {
num: this.limit,
})
);
},
onError(err) {
this.$modal.msgError(this.$t("upload.uploadFailed"));
},
onSuccess(res) {
this.fileProgress(res.data);
this.uploadList.push(res.data);
if (this.uploadList.length === this.number) {
this.fileList = this.fileList.concat(this.uploadList);
this.uploadList = [];
this.number = 0;
if (
this.beforeUploadingDisplayData.length ==
this.fileList.length
) {
this.$modal.msgSuccess(this.$t("upload.uploadSuccessful"));
this.$emit("input", this.fileList);
}
}
},
fileProgress(res) {
this.beforeUploadingDisplayData.forEach((i) => {
if (i.fileName == res.fileName && i.fileSize == res.fileSize) {
i.totalProgress = 100;
}
});
},
handleDelete(index) {
this.fileList.splice(index, 1);
this.beforeUploadingDisplayData.splice(index, 1);
this.$emit("input", this.fileList);
},
async uploadFile(file) {
if (!file) {
return;
}
const uid = file.uid;
var uploadFile = {};
uploadFile.name = file.name;
uploadFile.size = file.size;
uploadFile.chunkList = null;
uploadFile.file = file;
uploadFile.uploadingStop = false;
this.$set(this.fileUid, uid, {
currentChunk: 0,
totalChunks: 0,
totalProgress: 0,
});
var md5 = await this.computeMd5(file, uploadFile);
var res = await checkMd5(md5);
if (res.data) {
this.fileUid[uid].totalProgress = 100;
let arr = [res.data];
this.fileList = this.fileList.concat(arr);
if (
this.beforeUploadingDisplayData.length ==
this.fileList.length
) {
this.$modal.msgSuccess(this.$t("upload.uploadSuccessful"));
this.$emit("input", this.fileList);
}
return;
}
const chunkRequests = [];
const list = [];
this.fileUid[uid].totalChunks = Math.ceil(
file.size / this.chunkSize
);
for (let i = 0; i < this.fileUid[uid].totalChunks; i++) {
const start = i * this.chunkSize;
const end = Math.min(start + this.chunkSize, file.size);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append("index", i);
formData.append("file", chunk);
formData.append("chunkTotal", this.fileUid[uid].totalChunks);
formData.append("chunkSize", this.chunkSize);
formData.append("fileSize", file.size);
formData.append("fileName", file.name);
let data = uploadChunk(formData).then((res) => {
if (res.code === 200) {
this.fileUid[uid].currentChunk++;
if (
this.fileUid[uid].currentChunk >
this.fileUid[uid].totalChunks
) {
this.fileUid[uid].currentChunk =
this.fileUid[uid].totalChunks;
}
const chunkProgress =
((i + 1) / this.fileUid[uid].totalChunks) * 0.7;
const totalProgress = Math.round(
(chunkProgress + 0.2) * 100
);
if (totalProgress > this.fileUid[uid].totalProgress) {
this.$set(
this.fileUid[uid],
"totalProgress",
totalProgress
);
}
list.push(res.md5);
}
});
chunkRequests.push(data);
}
Promise.all(chunkRequests)
.then(() => {
if (list.length === this.fileUid[uid].totalChunks) {
this.mergeChunks(
list,
this.fileUid[uid].totalChunks,
md5,
file,
uid
);
} else {
this.$modal.msgWarning("上传失败,重新上传");
}
})
.catch((error) => {
console.error(error);
});
},
computeMd5(file, uploadFile) {
return new Promise((resolve, reject) => {
const chunkSize = this.chunkSize;
const fileReader = new FileReader();
const md5 = new SparkMD5();
let index = 0;
const loadFile = (uploadFile) => {
const slice = file.slice(index, index + chunkSize);
fileReader.readAsBinaryString(slice);
};
loadFile(uploadFile);
fileReader.onload = (e) => {
md5.appendBinary(e.target.result);
if (index < file.size) {
index += chunkSize;
loadFile(uploadFile);
} else {
resolve(md5.end());
}
};
});
},
checkMd5(md5) {
getSoftwareVersion(md5).then((response) => {
return respose;
});
},
mergeChunks(list, totalChunks, md5, file, uid) {
const formattedList = list.join(",");
const formData = new FormData();
formData.append("md5s", formattedList);
formData.append("chunkTotal", totalChunks);
formData.append("fileSize", file.size);
formData.append("fileName", file.name);
formData.append("fileMd5", md5);
mergeChunks(formData).then((res) => {
if (res.code === 200) {
let arr = [res.data];
this.fileList = this.fileList.concat(arr);
this.fileUid[uid] = {
currentChunk: 0,
totalChunks: 0,
totalProgress: 100,
};
if (
this.beforeUploadingDisplayData.length ==
this.fileList.length
) {
this.$modal.msgSuccess(
this.$t("upload.uploadSuccessful")
);
this.$emit("input", this.fileList);
}
}
});
},
promptStatement(totalProgress) {
if (totalProgress == 0) {
return "校验中";
} else if (totalProgress == 100) {
return "已上传";
} else {
return "上传中";
}
},
beforeRemove(file, fileList) {},
},
};
</script>
<style scoped lang="scss">
::v-deep .el-link--inner {
display: flex;
align-items: center;
}
.upload-progress {
display: flex;
align-items: center;
}
</style>