uploader.vue:
<template>
<div id="page">
<div class="main">
<uploader ref="uploader" :options="options" :file-status-text="fileStatusText" :autoStart="false" @file-added="onFileAdded" @file-progress="onFileProgress" @file-success="onFileSuccess" @file-error="onFileError" class="uploader">
<uploader-unsupport></uploader-unsupport>
<uploader-drop>
<uploader-btn class="upfile"><i class="iconfont icon-upload"></i> 上传文件</uploader-btn>
</uploader-drop>
<uploader-list></uploader-list>
</uploader>
</div>
</div>
</template>
<script>
import axios from 'axios';
import SparkMD5 from 'spark-md5'
import { Merge } from "@/api/api.js"
export default {
data() {
return {
fileMD5: '',
identifier: '',
options: {
target: this.$TF.baseURL + 'api/file/UploadChunk',//
testChunks: false,
chunkSize: 2097152, //2MB
simultaneousUploads: 1, //并发上传数
headers: {},
query: {
identifier: ''
},
maxChunkRetries: 1, //最大自动失败重试上传次数
parseTimeRemaining: function (timeRemaining, parsedTimeRemaining) { //格式化时间
return parsedTimeRemaining
.replace(/\syears?/, '年')
.replace(/\days?/, '天')
.replace(/\shours?/, '小时')
.replace(/\sminutes?/, '分钟')
.replace(/\sseconds?/, '秒')
}
},
statusTextMap: {
success: '上传成功',
error: '上传出错了',
uploading: '上传中...',
paused: '暂停',
waiting: '等待中...',
cmd5: '计算md5...'
},
fileStatusText: (status, response) => {
return this.statusTextMap[status];
},
}
},
created() {
this.options.headers.User = this.$TF.upHeaders()
//const uploaderInstance = this.$refs.uploader.uploader;
},
mounted() {
this.$nextTick(() => {
let u = document.querySelector('.uploader-btn')
let ipt = u.querySelector('input')
ipt.setAttribute('accept', '.xls,.xlsx')
})
},
methods: {
onFileAdded(file) {
file.pause();
let fileSize = 1 * 1024 * 1024 * 1024
// 计算MD5
if (fileSize < file.size) {
this.$message({ message: '文件不能大于1G', type: 'error' })
return
}
this.computeMD5(file);
},
chkMd5(file) {
let time = new Date().getTime();
let fileReader = new FileReader();
let spark = new SparkMD5(); //创建md5对象(基于SparkMD5)
fileReader.readAsBinaryString(file.file);
//文件读取完毕之后的处理
fileReader.onload = (e) => {
spark.appendBinary(e.target.result);
let md5 = spark.end();
// console.log(`MD5计算完成:${file.name} \nMD5:${md5} \n用时:${new Date().getTime() - time} ms`);
spark.destroy();
};
},
//计算MD5
computeMD5(file) {
let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice,
chunkSize = 2097152,
chunks = Math.ceil(file.size / chunkSize),
currentChunk = 0,
spark = new SparkMD5.ArrayBuffer(),
fileReader = new FileReader();
let time = new Date().getTime();
// console.log('计算MD5...')
file.cmd5 = true;
fileReader.onload = (e) => {
spark.append(e.target.result); // Append array buffer
currentChunk++;
if (currentChunk < chunks) {
// let md5 = spark.end();
// this.options.query.md5 = md5
// console.log(`第${currentChunk}分片解析完成,md5:${md5}, 开始第${currentChunk + 1} / ${chunks}分片解析`)
// spark.destroy(); //释放缓存
// file.uniqueIdentifier = md5; //将文件md5赋值给文件唯一标识
// console.log(file,md5);
// file.cmd5 = false; //取消计算md5状态
// file.resume(); //开始上传
// let percent = Math.floor(currentChunk / chunks * 100);
// console.log(percent);
// file.cmd5progress = percent;
loadNext();
} else {
// console.log('finished loading');
let md5 = spark.end();
this.fileMD5 = md5
this.identifier = this.$TF.guid()
this.options.query.identifier = this.identifier
spark.destroy(); //释放缓存
// file.uniqueIdentifier = md5; //将文件md5赋值给文件唯一标识
file.cmd5 = false; //取消计算md5状态
file.resume(); //开始上传
// console.log(file);
}
};
fileReader.onerror = () => {
file.cancel();
};
let loadNext = () => {
let start = currentChunk * chunkSize,
end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
};
loadNext();
},
// 文件进度的回调
onFileProgress(rootFile, file, chunk) {
// console.log(`上传中 ${file.name},chunk:${chunk.startByte / 1024 / 1024} ~ ${chunk.endByte / 1024 / 1024}`)
},
onFileSuccess(rootFile, file, response, chunk) {
let resp = JSON.parse(response);
if (resp.state == 0) {
let obj = {
fileName: file.name,
identifier: this.identifier,
totalSize: file.size,
totalChunks: chunk.offset + 1,
md5: this.fileMD5
}
Merge(obj).then((res) => {
if (res.data.state == 0) {
this.$emit('func', res.data)
} else {
}
});
} else {
}
},
onFileError(rootFile, file, response, chunk) {
},
},
}
</script>
<style scoped lang="scss">
.main {
max-width: 1000px;
background: #fff;
padding: 10px;
h2 {
padding: 30px 0;
text-align: center;
font-size: 20px;
}
}
.uploader {
width: 100%;
padding: 15px;
font-size: 14px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.4);
box-sizing: border-box;
.uploader-btn {
margin-right: 4px;
color: #fff;
padding: 6px 16px;
}
.upfile {
border: 1px solid #409eff;
background: #409eff;
}
.updir {
border: 1px solid #67c23a;
background: #67c23a;
}
.uploader-list {
max-height: 440px;
overflow: auto;
overflow-x: hidden;
overflow-y: auto;
height: 356px;
/deep/.iconfont {
font-size: 18px;
color: #409eff;
}
.no-file {
text-align: center;
font-size: 14px;
padding-top: 50px;
color: #ccc;
}
}
}
//手机等小屏幕手持设备。当设备宽度 在 320px和768px之间时,执行当前的css
@media only screen and (min-width: 320px) and (max-width: 768px) {
.uploader {
width: 98%;
padding: 0;
box-shadow: none;
}
}
</style>