html – 以弹窗的形式
<!-- 上传算法文件 -->
<el-dialog title="上传算法文件" :visible.sync="uploadPop" width="60%" :close-on-click-modal="false" :before-close="closeUploadPop" append-to-body custom-class="upload_box">
<div class="upload_btn_box">
<el-upload
class="upload-demo"
ref="upload"
action=""
:show-file-list="false"
:http-request="requestFile"
:before-upload = "checkFileType"
v-if="isShowUploadBtn"
>
<el-button>上传压缩包</el-button>
</el-upload>
<el-button v-else :loading="true">上传压缩包</el-button>
</div>
<div class="tip_box">
<span class="tips_text">
1、请上传文件类型为zip的算法文件;
2、上传可能需要一些时间,上传过程中建议不要关闭弹窗。
</span>
</div>
<div class="file_box">
<div class="upload_list" v-if="isShowUpload">
<div id="progress-bar-container" class="list_box" :class="{'isSuccess': uploadAllData.isSuccess}">
<span style="width: 14px"><i class="el-icon-folder"></i></span>
<span style="width: 30%">{{ uploadAllData.name }}</span>
<span style="width: 12%">{{ uploadAllData.type == 1? '算法文件': '测试集文件' }}</span>
<span style="width: 10%">{{ uploadAllData.size }}</span>
<span style="width: 16%">{{ uploadAllData.isSuccess?uploadAllData.msg: uploadAllData.isStop? '暂停中': uploadAllData.percent+'%' + ' ' + uploadAllData.speed + '/s' }}</span>
<span class="action_btn" v-if="!uploadAllData.isSuccess" @click="changeStop">{{ uploadAllData.isStop?'▶': '‖' }}</span>
<span class="action_btn" v-if="!uploadAllData.isSuccess" @click="changeClose">✖</span>
<div id="progress-bar" class="progress-bar" :style="{ width: progressBarWidth }"></div>
</div>
</div>
</div>
</el-dialog>
js – 初始化数据
// 初始化数据
initUpload() {
this.fileData = null;
this.chunks = [];
this.isShowUpload = false;
this.uploadAllData = {
id: null,
isSuccess: false,
isStop: false,
msg: '',
name: '',
size: '0',
speed: "0",
percent: 0,
type: 1,
};
this.progressBarWidth = '0%';
this.fileData = null;
this.chunkSize = 0;
this.fileSize = 0;
this.isShowUploadBtn = true;
this.uploadedChunks = 0;
},
js – 上传前判断文件类型
// 上传前判断
checkFileType(file) {
// 先初始化,预防在上传过程中继续点击上传
this.initUpload();
// 禁止继续点击上传
this.isShowUploadBtn = false;
// 判断只能上传.zip压缩包
const allowedExtensions = ['zip']; // 允许的文件后缀名列表
const extension = file.name.split('.').pop().toLowerCase(); // 获取文件的后缀名
if (allowedExtensions.indexOf(extension) === -1) {
this.$message.error('只允许上传zip格式的文件');
this.isShowUploadBtn = true;
return false; // 阻止文件上传
}
return true; // 允许文件上传
},
js – 上传前准备、获取传输任务(旧)
!说明: 因为后台需要文件的md5校验文件的完整性,所以需要读取文件,但是文件太大的时候,导致前端读取的时候就需要很长时间了,经协调,去掉参数md5
import SparkMD5 from 'spark-md5';
// 上传算法文件
requestFile(file) {
this.fileData = file.file;
const reader = new FileReader();
console.log('开始分片任务!');
reader.onload = (event) => {
const fileObj = event.target.result;
const spark = new SparkMD5.ArrayBuffer(); // 获取文件的md5 哈希值 -- 后台用于验证文件的完整性
console.log(spark, 'md5');
spark.append(fileObj);
let md5Hash = spark.end();
console.log(md5Hash, 'md5');
spark.destroy(); //释放缓存
// 将文件分片
this.chunkSize = 1024 * 1024 * 20 // 每个分片的大小20M
this.fileSize = this.fileData.size; // 文件总大小
this.chunkCount = Math.ceil(this.fileSize / this.chunkSize) // 分片数量
this.uploadedChunks = 0 // 重置已上传的分片数量
this.startTime = new Date().getTime() // 记录上传开始时间
// 逐个上传分片
for (let index = 0; index < this.chunkCount; index++) {
const start = index * this.chunkSize
const end = Math.min(start + this.chunkSize, this.fileSize)
const chunk = this.fileData.slice(start, end)
this.chunks.push(chunk)
}
console.log(this.chunks);
console.log(this.fileData);
// 整合上传前的参数,请求获取id
let beforeUpload = {
belongsId: this.algorithmVersionId,
chunkNumberCount: this.chunkCount,
fileSavePath: "",
fileType: 1, // 1为算法文件; 2为测试集
md5: md5Hash,
pauseOffset: 0,
status: 1,
transmitType: 1,
uploaded: this.uploadedChunks,
fileName: this.fileData.name
}
console.log(beforeUpload, '参数整合');
httpPost('/api/v1/acctm_file_transmit_task', beforeUpload) // 文件分片传输任务表
.then(res => {
if(res.code == '10000') {
this.currentUploadId = res.data;
let num = (this.fileSize/(1024*1024)).toFixed(2); // B => MB
let fileSizeStr = "";
if(num >= 1024) {
fileSizeStr = num + 'GB'
} else if(1 <= num < 1024 ) {
fileSizeStr = num + 'MB'
} else {
fileSizeStr = this.fileSize + 'KB'
}
this.uploadChunk(this.chunks, 0, this.chunkSize, this.fileData.name, fileSizeStr)
} else {
this.isShowUploadBtn = true;
this.$message({
type: 'error',
message: '文件分片传输失败!'
})
}
})
.catch(() => {
console.log('到这人了?');
})
};
reader.readAsArrayBuffer(this.fileData);
将文件分片
this.chunkSize = 1024 * 1024 * 20 // 每个分片的大小20M
this.fileSize = this.fileData.size; // 文件总大小
this.chunkCount = Math.ceil(this.fileSize / this.chunkSize) // 分片数量
this.uploadedChunks = 0 // 重置已上传的分片数量
this.startTime = new Date().getTime() // 记录上传开始时间
// 逐个上传分片
for (let index = 0; index < this.chunkCount; index++) {
const start = index * this.chunkSize
const end = Math.min(start + this.chunkSize, this.fileSize)
const chunk = this.fileData.slice(start, end)
this.chunks.push(chunk)
}
let beforeUpload = {
belongsId: this.algorithmVersionId,
chunkNumberCount: this.chunkCount,
fileSavePath: "",
fileType: 1, // 1为算法文件; 2为测试集
pauseOffset: 0,
status: 1,
transmitType: 1,
uploaded: this.uploadedChunks,
fileName: this.fileData.name
}
httpPost('/api/v1/acctm_file_transmit_task', beforeUpload) // 文件分片传输任务表
.then(res => {
if(res && res.code == '10000') {
this.currentUploadId = res.data;
let num = (this.fileSize/(1024*1024)).toFixed(2); // B => MB
let fileSizeStr = "";
if(num >= 1024) {
fileSizeStr = (num/1024).toFixed(2) + 'GB'
} else if(1 <= num && num < 1024 ) {
fileSizeStr = num + 'MB'
} else {
fileSizeStr = (num*1024).toFixed(2) + 'KB'
}
this.uploadChunk(this.chunks, 0, this.chunkSize, this.fileData.name, fileSizeStr)
} else {
this.isShowUploadBtn = true;
}
})
.catch(() => {
console.log('开始分片任务失败!');
})
},
js – 上传前准备、获取传输任务(目前采用)
// 上传算法文件
requestFile(file) {
this.fileData = file.file;
// 将文件分片
this.chunkSize = 1024 * 1024 * 20 // 每个分片的大小20M
this.fileSize = this.fileData.size; // 文件总大小
this.chunkCount = Math.ceil(this.fileSize / this.chunkSize) // 分片数量
this.uploadedChunks = 0 // 重置已上传的分片数量
this.startTime = new Date().getTime() // 记录上传开始时间
// 逐个上传分片
for (let index = 0; index < this.chunkCount; index++) {
const start = index * this.chunkSize
const end = Math.min(start + this.chunkSize, this.fileSize)
const chunk = this.fileData.slice(start, end)
this.chunks.push(chunk)
}
let beforeUpload = {
belongsId: this.activeRouter.id,
chunkNumberCount: this.chunkCount,
fileSavePath: "",
fileType: 2, // 1为算法文件; 2为测试集
pauseOffset: 0,
status: 1,
transmitType: 1,
uploaded: this.uploadedChunks,
fileName: this.fileData.name
}
httpPost('/api/v1/acctm_file_transmit_task', beforeUpload) // 文件分片传输任务表
.then(res => {
if(res && res.code == '10000') {
this.currentUploadId = res.data;
let num = (this.fileSize/(1024*1024)).toFixed(2); // B => MB
let fileSizeStr = "";
if(num >= 1024) {
fileSizeStr = (num/1024).toFixed(2) + 'GB'
} else if(1 <= num && num < 1024 ) {
fileSizeStr = num + 'MB'
} else {
fileSizeStr = (num*1024).toFixed(2) + 'KB'
}
this.uploadChunk(this.chunks, 0, this.chunkSize, this.fileData.name, fileSizeStr)
} else {
this.isShowUploadBtn = true;
}
})
.catch(() => {
console.log('开始分片任务失败!');
})
},
js – 开始分片上传
!注意:自己封装的axios 请求触发不了onUploadProgress事件
// 上传分片
uploadChunk(chunks, index, chunkSize, name, fileSize) {
console.log(chunks, index);
this.isShowUpload = true;
this.isShowUploadBtn = true; // 显示加载文件后放开上传的按钮
this.progressBar = document.getElementById('progress-bar');
if (index >= chunks.length) {
// 全部分片上传完成
this.uploadAllData = {
id: this.currentUploadId,
isStop: false,
isSuccess: true,
msg: '上传完成',
name: name,
size: fileSize,
speed: '0',
percent: 0,
type: 1
}
// 重置数据
this.chunks = [];
this.progressBarWidth = '0%';
this.fileData = null;
this.chunkSize = 0;
this.fileSize = 0;
this.uploadedChunks = 0;
return
}
let chunk = chunks[index]; // 当前文件的分片数据
// 整合参数
const formData = new FormData()
formData.append('chunkSize', chunkSize)
formData.append('file', chunk, index+1) // 文件的分片,从1开始 -- 与后台协商,文件的命名
formData.append('chunkNumber', index+1); // 分片序号,从1开始 -- 与后台协商
formData.append('chunkNumberCount', this.chunkCount)
formData.append('transmitTaskId', this.currentUploadId)
// 发送分片请求
let url = process.env.VUE_APP_BASE_API;
axios.post(url + '/api/v1/acctm_file_transmit_task/chunkUpload', formData, { // 文件分片信息表
headers: {
'Content-Type': 'multipart/form-data',
'Authorization': sessionStorage.getItem('token') || ""
},
onUploadProgress: (progressEvent) => {
const uploaded = progressEvent.loaded
// const total = progressEvent.total
// 更新上传进度
this.progress = Math.round(((index + 1) / this.chunkCount) * 100)
// this.progress = Math.round((uploaded / total) * 100)
// 计算上传速度
const currentTime = new Date().getTime()
const elapsedTime = (currentTime - this.startTime) / 1000 // 经过的时间(秒)
this.speed = Math.round((uploaded - this.uploadedSize) / elapsedTime / 1024) // 上传速度(KB/s)
// console.log(this.speed);
// 更新已上传大小
this.uploadedSize = uploaded;
// 转换单位,超过1M,显示M,超过G,显示G
let number = (this.speed/1024).toFixed(2); // KB => MB
// console.log(1 <= number < 1024);
let speedStr = ""
if(number >= 1024) {
speedStr = (number/1024).toFixed(2) + 'GB'
} else if(1 <= number && number < 1024) {
speedStr = number + 'MB'
} else if(number < 0 || this.speed < 0) {
speedStr = 0 + 'KB';
} else {
speedStr = (this.speed).toFixed(2) + 'KB'
}
this.uploadAllData = {
id: this.currentUploadId,
isStop: false,
isSuccess: false,
msg: '正在下载',
name: name,
size: fileSize,
speed: speedStr,
percent: this.progress == 100? 99: this.progress,
type: 1
};
this.progressBarWidth = `${this.progress}%`
}
})
.then((res) => {
// console.log(res);
if(res && res.data && res.data.code == '10000' && res.data.data) {
if(this.timer) {
clearTimeout(this.timer)
}
this.uploadedChunks++
// 上传下一个分片
this.uploadChunk(chunks, this.uploadedChunks, chunkSize, name, fileSize);
} else if(res && res.data && res.data.code == '10000' && !res.data.data) { // 重新上传当前分片
this.uploadChunk(chunks, this.uploadedChunks, chunkSize, name, fileSize);
} else if(res && res.data && res.data.code == '40004') {
this.uploadAllData.isStop = !this.uploadAllData.isStop;
return
} else if(res && res.data && res.data.code == '50010') {
this.uploadAllData.isStop = !this.uploadAllData.isStop;
return
} else {
// 一般是网络问题,隔几秒继续上传
this.timer = setTimeout(() => {
this.uploadChunk(chunks, this.uploadedChunks, chunkSize, name, fileSize);
}, 2000);
}
})
.catch((error) => {
// 处理上传错误
console.log('上传错误', error)
})
},
js – 暂停\启动
!说明:因为前端不能做到真正的把请求暂停,只能调用接口,然后后台去实际暂停上传的接口
// 暂停、启动
changeStop() {
if(this.timer) {
clearTimeout(this.timer)
}
this.uploadAllData.isStop = !this.uploadAllData.isStop;
if(this.uploadAllData.isStop) { // 点击了启动图标,触发暂停
httpGet("/api/v1/acctm_file_transmit_task/pauseStart/" + this.currentUploadId)
.then(res => {
if(res && res.code == '10000' && !res.data) {
// console.log('暂停成功!');
}
})
} else { // 点击了暂停图标,触发启动
httpGet("/api/v1/acctm_file_transmit_task/pauseStart/" + this.currentUploadId)
.then(res => {
if(res && res.code == '10000') {
let num = (this.fileSize/(1024*1024)).toFixed(2); // B => MB
let fileSizeStr = "";
if(num >= 1024) {
fileSizeStr = (num/1024).toFixed(2) + 'GB'
} else if(1 <= num && num < 1024 ) {
fileSizeStr = num + 'MB'
} else {
fileSizeStr = (num*1024).toFixed(2) + 'KB'
}
// 从之前的分片
this.uploadChunk(this.chunks, this.uploadedChunks+1, this.chunkSize, this.fileData.name, fileSizeStr);
}
})
}
},
js – 删除任务
changeClose() {
if(this.timer) {
clearTimeout(this.timer)
}
httpDeleter('/api/v1/acctm_file_transmit_task/' + this.currentUploadId)
.then(res => {
if(res && res.code == '10000') {
this.$message({
type: 'success',
message: '删除成功!'
});
this.initUpload();
}
})
},
js – 关闭弹窗
!说明: 由于上传的文件比较大,本地文件存储的位置获取不到,所以如果半路退出,则不能再继续获取到上一次的文件
closeUploadPop() {
if(this.timer) {
clearTimeout(this.timer)
}
// 判断是否上传完成
if(this.fileData && !this.uploadAllData.isSuccess) {
this.$confirm('文件上传未完成,关闭弹窗则上传无效,是否确定关闭?', '温馨提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(async() => {
let res = await httpDeleter('/api/v1/acctm_file_transmit_task/' + this.currentUploadId)
if(res && res.code == '10000') {
this.uploadPop = false;
this.initUpload();
if(this.activeName == "1"){
this.getCloudData(1,10,1,this.algorithmId);
} else {
this.getCloudData(1,10,2,this.algorithmId);
}
}
})
.catch(() => {
})
} else {
this.uploadPop = false;
// 格式化数据
this.initUpload();
if(this.activeName == "1"){
this.getCloudData(1,10,1,this.algorithmId);
} else {
this.getCloudData(1,10,2,this.algorithmId);
}
}
},
完成、效果展示
正在上传
暂停
退出
完成