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-uploadclass="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 // 每个分片的大小20Mthis.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.chunkSizeconst 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);// 整合上传前的参数,请求获取idlet 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 => MBlet 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 // 每个分片的大小20Mthis.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.chunkSizeconst 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 => MBlet 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 // 每个分片的大小20Mthis.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.chunkSizeconst 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 => MBlet 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,显示Glet 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 => MBlet 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);}}
},
完成、效果展示
正在上传
暂停
退出
完成