实现断点续传主题思路:
对文件进行每200MB切片,基于spark-md5计算出第一个切片文件的hash值,将文件的名称,大小,hash值传给后台,后台确认当前文件是否上传过,已上传过返回之前上传大小或第几个切片,前端再次上传切片,直到上传完成.
代码:
<el-upload
class="upload-demo"
ref='upload'
action=""
:http-request='calcMd5'
:on-preview="handlePreview"
:on-remove="handleRemove"
:before-remove="beforeRemove"
:limit="1"
:on-exceed="handleExceed"
:file-list="fileList">
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
<script>
export default {
data() {
return {
fileList: [],
chunkSize :1024*1024*200,//200MB
source:null,
uploadFileData:{
download_chunk:0,//将上传第几个切片 0 为第一个
md5:',
file:null,
},
};
},
methods: {
calcMd5(myFile){
let blobSlice =
File.prototype.slice ||
File.prototype.mozSlice ||
File.prototype.webkitSlice,
file = myFile.file,
chunkSize = this.chunkSize, // Read in chunks of 2MB
chunks = Math.ceil(file.size / chunkSize),
currentChunk = 0,
spark = new SparkMD5.ArrayBuffer(),
that=this,
fileReader = new FileReader();
fileReader.onload = function (e) {
console.log("read chunk nr", currentChunk + 1, "of", chunks);
spark.append(e.target.result); // Append array buffer
currentChunk++;
//文件太大 计算整个文件耗时太久 so 只计算第一个切片的hash
if (currentChunk >= 1) {
let md5= spark.end();
that.uploadFileData.file=file;
that.uploadFileData.md5=md5;
that.searchFile();
console.log("finished loading");
console.info("computed hash", spark.end()); // Compute hash
} else {
loadNext();
}
fileReader.onerror = function () {
console.warn("oops, something went wrong.");
};
function loadNext() {
var start = currentChunk * chunkSize,
end = start + chunkSize >= file.size ? file.size : start + chunkSize;
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
}
loadNext();
},
async searchFile(){
let obj={
md5: this.uploadFileData.md5,
size: this.uploadFileData.file.size,
name:this.uploadFileData.file.name,
}
let {data:res}=await this.$axios.post('rul',obj);
if(res.code!==0){
this.$message.error(res.msg);
return;
}
if(res.data.completed){ //判断是否已上传
if(this.$refs.upload){
this.$refs.upload.clearFiles(); //已上传清理文件
}
this.$message.success('已上传!');
return;
}
//后台返回下一个上传下标 或者返回大小前端计算
this.uploadFileData.download_chunk=res.data.download_chunk;
//继续上传
this.chunsFile();
},
async chunsFile(){
this.source=this.$axios.CancelToken.source();//可用于取消正在上传的接口
let config={
cancelToken:this.source.token,
headers:{
"Content-Type":"multipart/form-data"
},
}
let chunkLength=0;//文件切片后的长度
//获取分片后的长度
if (this.uploadFileData.file.size > this.chunkSize) {
chunkLength = Math.ceil(this.uploadFileData.file.size / this.chunkSize);
} else {
chunkLength = 1
}
//用formData通过ajax2上传文件内容
let fm = new FormData();
fm.append('md5', this.uploadFileData.md5);//spark-md5计算出来的md5值
fm.append('size',this.uploadFileData.file.size);
fm.append('download_chunk',this.uploadFileData.download_chunk);
fm.append('name ', this.uploadFileData.file.name);
fm.append('file', this.uploadFileData.file.slice(this.uploadFileData.download_chunk *this.chunkSize, (this.uploadFileData.download_chunk + 1) * this.chunkSize);
let {data:res}=await this.$axios.post('url',fm,config);
if(res.code!==0){
this.$message.error(res.msg);
return;
}
if(res.data.completed){ //判断是否已上传
if(this.$refs.upload){
this.$refs.upload.clearFiles(); //已上传清理文件
}
this.$message.success(res.msg);
}else{
this.uploadFileData.download_chunk=res.data.download_chunk;
this.chunsFile(); //继续上传下个切片
}
},
handleRemove(file, fileList) {
console.log(file, fileList);
},
handlePreview(file) {
console.log(file);
},
handleExceed(files, fileList) {
this.$message.warning(`当前限制选择 1 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`);
},
beforeRemove(file, fileList) {
return this.$confirm(`确定移除 ${ file.name }?`);
}
}
}
</script>