公司要求实现的功能,大概思路是将上传的文件通过jsZip压缩后,进行切片,留一下总切片,当前片,并把这些数据给后端,至于前端的校验,是由Md5完成的,验证文件唯一性,这样下次上传该文件的时候就会略过并更新已经上传过的切片,开始下一片的续传。
首先是创建一个上传按钮,绑定上传事件,我这边是需要放在表格里,并且需要是base64格式所以有个转换,具体看个人需求:
importModel() {
this.fileIndex = 0
const input = document.createElement('input');
input.type = 'file';
input.onchange = (event) => {
let selectedFiles = this.fileFilter(event.target.files);
if (selectedFiles.length > 0) {
const zip = new JSZip();
for (let i = 0; i < selectedFiles.length; i++) {
zip.file(selectedFiles[i].name, selectedFiles[i], { date: new Date(2023, 0, 1) });
}
zip.generateAsync({ type: "base64" })
.then((content) => {
zip.generateAsync({ type: "uint8array" }).then(content1 => {
this.fileData = [
{ file: content, fileMd5: md5(content1), zipInfo: {}, type: '文件名 '+ '.zip', std: '50%', address: '删除', progressValue: 0 }
];
})
});
this.compress = false;
}
else {
this.$message.error('请上传有效文件');
}
};
input.click();
},
接下来是上传的方法 (
uploadFiles() {
if (this.fileIndex < this.fileData.length)
//下面这个接口是跟后端约定好的需要的参数,个人需求可以不用走,这边拿到返回的id再带给下一个方法,
实际接口({
size://压缩后返回的大小,
fileName: 实际上传的文件名//
})
.then(response => {
this.indexedDBHelper.get(this.fileData[this.fileIndex].fileMd5).then((d) => {
if (d)
this.fileData[this.fileIndex].zipInfo = d.data
else
this.fileData[this.fileIndex].zipInfo = {
id: 后端带回来的id,看个人需求,
fileSize:this.fileData[this.fileIndex].file.length,
chunkSize: 4 * 1024 * 1024,
chunkCount: Math.ceil(文件大小 / (4 * 1024 * 1024)),
currentChunk: 0, //初始上传切片
}
this.uploadNextChunk()
})
});
else
console.log('全部上传完成')
},
///上传文件切片
uploadNextChunk() {
let { currentChunk, chunkCount } = this.fileData[this.fileIndex].zipInfo
if ((currentChunk) < chunkCount) {
this.uploadChunk().then(response => {
this.showConnectingMessage = false;
if (response?.data) {
this.$set(this.fileData[this.fileIndex], 'progressValue', Math.round(((currentChunk + 1) / chunkCount) * 100));//更新进度条
this.fileData[this.fileIndex].zipInfo.currentChunk += 1
this.uploadNextChunk();
this.indexedDBHelper.set({ id: this.fileData[this.fileIndex].fileMd5, data: this.fileData[this.fileIndex].zipInfo })
//保存md5及信息到indexDB
}
});
} else {
this.$set(this.fileData[this.fileIndex], 'progressValue', 100);
this.indexedDBHelper.delete(this.fileData[this.fileIndex].fileMd5)
//上传完成,清空md5
this.fileIndex += 1
this.uploadFiles()
}
},
///调用后端接口
uploadChunk() {
let start = (this.fileData[this.fileIndex].zipInfo.currentChunk) * this.fileData[this.fileIndex].zipInfo.chunkSize;
let end = Math.min(this.fileData[this.fileIndex].zipInfo.fileSize, start + this.fileData[this.fileIndex].zipInfo.chunkSize);
let chunkFile = this.fileData[this.fileIndex].file.slice(start, end);
const uploadData = {
id: this.fileData[this.fileIndex].zipInfo.id,
fileBase64: 'Base64,' + chunkFile,
fileName: this.fileData[this.fileIndex].zipInfo.fileName,
size: chunkFile.length,
chunks: this.fileData[this.fileIndex].zipInfo.chunkCount,
chunk: this.fileData[this.fileIndex].zipInfo.currentChunk,
md5: this.fileData[this.fileIndex].flieMd5,
};
return 后端接口(uploadData); //将数据发送给后端接口
},
最后是删除方法,绑定在按钮上就可以
deleteItem() {
MessageBox.confirm('确定删除该文件吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
后端接口(this.fileData[this.fileIndex].zipInfo.id)//将需要删除的id发送给后端
.then(response => {
const index = this.fileData.findIndex(item => item.id === this.fileData[this.fileIndex].zipInfo.id);
if (index !== -1) {
this.fileData.splice(index, 1);
}
})
.catch(error => {
console.error('删除文件时出错:', error);
});
}).catch(() => {
});
},
虽然这个功能可以实现续传,整个流程也通了,但是有个问题就是无法承担太大的文件,如果需求是中小文件的话是可以用的,超大文件的话会崩。