用到的插件只有el-upload和 spark-md5
<template>
<div>
<el-upload
:action="dataUrl"
accept=".gz"
:on-change="handleBeforeUpload"
:on-progress="handleUploadProgress"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
:file-list="fileList"
:on-remove="handleRemove"
:data="uploadData"
:headers="uploadHeaders"
:limit="1"
:multiple="false"
:show-file-list="true"
:auto-upload="false"
:disabled="uploading"
:with-credentials="true"
:on-exceed="handleUploadExceed"
>
<div class="down_harbor">
<i class="el-icon-top icon"></i>
<el-button size="small" :disabled="uploading" type="text">
{{ uploading ? "镜像文件上传..." : "镜像文件上传" }}
</el-button>
</div>
</el-upload>
<el-progress :percentage=progressPercentage v-if="uploading"></el-progress>
</div>
</template>
<script>
import request from "@/utils/request";
import { getFileMd5 } from "../../toolChain/lora/chunkFile.js";
export default {
props: {
name:{
type:String,
default:''
},
tag:{
type:String,
default:''
}
},
data() {
return {
fileSize:'',
uploading: false,
fileList: [],
uploadData: {},
md5: null,
dataUrl: `${process.env.VUE_APP_BASE_API}/file/mirror/breakpoint-upload`,
uploadHeaders: {},
chunkSize: 5 * 1024 * 1024, // 5MB
maxFileSize: 1000000 * 1024 * 1024, // 1000000MB
progressPercentage: 0
};
},
methods: {
//上传之前
async handleBeforeUpload(file) {
this.fileSize=file.size
this.md5 = await getFileMd5(
new Blob([file], { type: file.type }),
Math.ceil(file.size / this.chunkSize)
);
if (file.size > this.maxFileSize) {
this.$message.error(
`文件大小不能超过 ${this.maxFileSize / 1024 / 1024}MB.`
);
return false;
}
this.checkfile(file)
this.fileList.push(file);
await this.handleUpload(file);
return false;
},
//检查文件是否已经上传
checkfile(file) {
request({
url: `/file/mirror/check-file?fileName=${file.name}&&md5=${this.md5}&&name=${this.name}&&tag=${this.tag}`,
method: "get",
})
.then((res) => {
if (res.code == 200 && res.msg == "文件已上传成功") {
this.$message.success("文件上传成功.");
return;
} else if (
res.code == 200 &&
res.msg == "检查到文件不存在,需要上传!"
) {
this.checkfile(file);
this.fileList.push(file);
this.handleUpload(file);
}
})
.catch((err) => {});
},
//删除已上传的文件
handleRemove(file) {
this.fileList=this.fileList.filter(item => item.name!=file.name)
},
//文件上传成功
async handleUploadSuccess(response, file) {
this.$emit("loadfile",this.fileList);
this.uploading = false;
this.$message.success("文件上传成功.");
},
//文件上传失败
handleUploadError(error, file) {
this.uploading = false;
this.$message.error(`文件上传失败`);
},
//进度条
handleUploadProgress(event, file, fileList) {
const progress = Math.round(event.percent);
file.progress = progress;
this.fileList = [...fileList];
this.progressVisible = true;
},
//文件超出个数限制时的钩子
handleUploadExceed(files, fileList) {
this.$message.error(`一次只能上传一个文件.`);
},
// 上传
async uploadChunks(file) {
const fileSize = file.size;
const chunksTotal = Math.ceil(fileSize / this.chunkSize); //分片总数
const fileName = file.name;
let chunksUploaded = 0;
let chunks = [];
for (let i = 0; i < chunksTotal; i++) {
const start = i * this.chunkSize;
const end = Math.min(fileSize, start + this.chunkSize);
chunks.push({
file: file.slice(start, end),
index: i,
start,
end,
size: end - start,
uploaded: false,
});
}
while (chunksUploaded < chunksTotal) {
const chunk = chunks[chunksUploaded];
if (!chunk.uploaded) {
const formData = new FormData();
formData.append("file", chunk.file);
formData.append("id", this.md5); //id
formData.append("md5", this.md5); //md5
formData.append("chunk", chunk.index); //第几分片
formData.append("chunks", chunksTotal); //总分片数量
// formData.append("size", this.chunkSize); //文件大小
formData.append("size", this.fileSize); //文件大小
formData.append("name", fileName); //文件名
formData.append("mirrorName", this.name); //镜像名称
formData.append("tag", this.tag); //镜像版本
try {
const response = await this.uploadChunk(formData);
if (response.code == 200) {
chunk.uploaded = true;
chunksUploaded++;
const progress = Math.round((chunksUploaded / chunksTotal) * 100);
file.progress = progress;
this.fileList = [...this.fileList];
} else {
throw new Error(
`上传分片 ${chunk.index} 失败: ${response.statusText}`
);
}
} catch (error) {
return;
}
} else {
chunksUploaded++;
}
}
},
//上传一片
async uploadChunk(formData) {
return new Promise((resolve, reject) => {
this.uploadFile(formData)
.then((response) => {
if (response.code == 200 && response.result == 200 ) {
const progress = Math.round(
(formData.get('chunk') / formData.get('chunks')) * 100
);
this.fileList[0].progress = progress;
this.progressPercentage=progress
this.fileList = [...this.fileList];
resolve(response);
} else if (response.code == 200 && response.result == 201 ) {
this.progressPercentage=100
this.handleUploadSuccess();
reject();
} else {
reject(this.$message.error(response.msg));
}
})
.catch((error) => {
console.log(error);
console.log("捕获到了错误");
this.$message.error("上传失败!");
});
});
},
uploadFile(params) {
return request({
url: `/file/mirror/breakpoint-upload`,
method: "post",
data: params,
});
},
async handleUpload(file) {
this.uploading = true;
this.$emit("isloading",true);
await this.uploadChunks(file.raw);
},
},
};
</script>
<style>
.down_harbor {
width: 221px;
height: 66px;
background: #eef4ff;
border-radius: 4px;
border: 1px solid #afceff;
font-size: 14px;
font-family: PingFangSC, PingFang SC;
font-weight: 500;
color: #4985f8;
text-align: center;
line-height: 66px;
}
</style>
md5
import SparkMD5 from 'spark-md5'
/**
* 获取文件MD5唯一标识码
* @param file
* @returns {Promise<unknown>}
*/
export function getFileMd5(file, chunkCount) {
return new Promise((resolve, reject) => {
const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
const chunks = chunkCount
let currentChunk = 0
const spark = new SparkMD5.ArrayBuffer()
const fileReader = new FileReader()
fileReader.onload = function(e) {
spark.append(e.target.result)
currentChunk++
if (currentChunk < chunks) {
loadNext()
} else {
const md5 = spark.end()
resolve(md5)
}
}
fileReader.onerror = function(e) {
reject(e)
}
function loadNext() {
const start = currentChunk * chunkSize
let end = start + chunkSize
if (end > file.size) {
end = file.size
}
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end))
}
loadNext()
})
}
// 切片大小,自定义
export const chunkSize = 5 * 1024 * 1024