项目中遇到,记录一下,前端使用vue-simple-upload + spark-md5 实现大文件分片上传,断点续传(附代码地址)。
- 安装依赖:
npm install vue-simple-uploader --save
npm install spark-md5
- main.js中引入vue-simple-uploader
import uploader from 'vue-simple-uploader';
...,
Vue.use(uploader)
- 新建FileUpload.vue组件
<template>
<div class="fileUpload">
<uploader
:autoStart="false"
:options="options"
:file-status-text="statusText"
class="uploader-example"
@file-complete="fileComplete"
@complete="complete"
@file-success="fileSuccess"
@file-added="fileAdded"
>
<uploader-unsupport></uploader-unsupport>
<uploader-drop>
<p>将文件拖放到此处以上传</p>
<uploader-btn>选择文件</uploader-btn>
<uploader-btn :attrs="attrs">选择图片</uploader-btn>
<uploader-btn :directory="true">选择文件夹</uploader-btn>
</uploader-drop>
<!-- <uploader-list></uploader-list> -->
<uploader-files></uploader-files>
</uploader>
<br />
<el-button @click="allStart()" :disabled="disabled">全部开始</el-button>
<el-button @click="allStop()" style="margin-left: 4px">全部暂停</el-button>
<el-button @click="allRemove()" style="margin-left: 4px"
>全部移除</el-button
>
</div>
</template>
<script>
import axios from "axios";
import SparkMD5 from "spark-md5";
export default {
name: "fileUpload",
data() {
return {
skip: false,
options: {
target: "http://localhost:8090/file/upload",
// 开启服务端分片校验功能
testChunks: true,
parseTimeRemaining: function(timeRemaining, parsedTimeRemaining) {
return parsedTimeRemaining
.replace(/\syears?/, "年")
.replace(/\days?/, "天")
.replace(/\shours?/, "小时")
.replace(/\sminutes?/, "分钟")
.replace(/\sseconds?/, "秒");
},
// 服务器分片校验函数
checkChunkUploadedByResponse: (chunk, message) => {
const result = JSON.parse(message);
if (result.data.skipUpload) {
this.skip = true;
return true;
}
return (result.data.uploaded || []).indexOf(chunk.offset + 1) >= 0;
}
// headers: {
// // 在header中添加的验证,请根据实际业务来
// "Access-Token": storage.get(ACCESS_TOKEN),
// },
},
attrs: {
accept: "image/*"
},
statusText: {
success: "上传成功",
error: "上传出错了",
uploading: "上传中...",
paused: "暂停中...",
waiting: "等待中...",
cmd5: "计算文件MD5中..."
},
fileList: [],
disabled: true
};
},
methods: {
fileSuccess(rootFile, file, response, chunk) {
const result = JSON.parse(response);
const fileInfo = {
identifier: file.uniqueIdentifier,
filename: file.name,
totalChunks: chunk.offset,
totalSize: file.size
};
// 是否需要合并
if (result.data.needMerge && !this.skip) {
axios
.post("http://localhost:8090/file/merge", fileInfo)
.then(res => {
if (res.code === 200) {
console.log("上传成功");
} else {
console.log(res);
}
})
.catch(function(error) {
console.log(error);
});
} else {
console.log("上传成功,不需要合并");
}
if (this.skip) {
this.skip = false;
}
},
fileComplete(rootFile) {},
complete() {},
fileAdded(file) {
//示例代码:此处可对上传文件的格式进行校验,如只允许上传后缀为xls的文件
// const extname = file.getExtension();
// if (extname !== "xls") {
// this.$message("请选择xls文件");
// file.ignored = true;
// } else {
// this.computeMD5(file);
// }
this.computeMD5(file);
},
computeMD5(file) {
let fileReader = new FileReader();
let time = new Date().getTime();
let blobSlice =
File.prototype.slice ||
File.prototype.mozSlice ||
File.prototype.webkitSlice;
let currentChunk = 0;
// 文件分片大小
const chunkSize = 5 * 1024 * 1024;
let chunks = Math.ceil(file.size / chunkSize);
let spark = new SparkMD5.ArrayBuffer();
// 文件状态设为"计算MD5"
file.cmd5 = true; //文件状态为“计算md5...”
file.pause();
loadNext();
fileReader.onload = e => {
spark.append(e.target.result);
if (currentChunk < chunks) {
currentChunk++;
loadNext();
// 实时展示MD5的计算进度
console.log(
`第${currentChunk}分片解析完成, 开始第${currentChunk +
1} / ${chunks}分片解析`
);
} else {
let md5 = spark.end();
console.log(
`MD5计算完毕:${file.name} \nMD5:${md5} \n分片:${chunks} 大小:${
file.size
} 用时:${new Date().getTime() - time} ms`
);
spark.destroy(); //释放缓存
file.uniqueIdentifier = md5; //将文件md5赋值给文件唯一标识
file.cmd5 = false; //取消计算md5状态
file.resume(); //开始上传
}
};
fileReader.onerror = function() {
this.error(`文件${file.name}读取出错,请检查该文件`);
file.cancel();
};
function loadNext() {
let start = currentChunk * chunkSize;
let end =
start + chunkSize >= file.size ? file.size : start + chunkSize;
fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
}
},
allStart() {
console.log(this.fileList);
this.fileList.map(e => {
if (e.paused) {
e.resume();
}
});
},
allStop() {
console.log(this.fileList);
this.fileList.map(e => {
if (!e.paused) {
e.pause();
}
});
},
allRemove() {
this.fileList.map(e => {
e.cancel();
});
this.fileList = [];
}
},
watch: {}
};
</script>
<style scoped></style>
- 页面使用Filepload组件
<file-upload/>
- 代码地址
https://gitee.com/mengchengxian/fileupload.git