前端大文件分片上传(Vue)
2022-04-14 本文已影响0人
symY_Y
list.vue
<template>
<el-button
type="primary"
:disabled="disabled || isDisabled"
:loading="buttonLoading"
@click="confirm()"
>{{ buttonLoading ? percentage + "%" : title }}</el-button
>
</template>
<script>
import { uploadByPieces } from "./comUpload";
export default {
props: {
title: {
type: String,
default: "保存",
},
disabled: {
type: Boolean,
default: true,
},
uploadSuccess: {
type: Boolean,
default: true,
},
selectFile: {
type: Object,
default: () => {},
},
url: {
type: Function,
default: null,
},
// 数据校验
checkMsg: {
type: Function,
default: null,
},
// 数据保存
submitMsg: {
type: Function,
default: null,
},
submitMsgStep: {
type: Function,
default: null,
},
},
data() {
return {
buttonLoading: false,
isDisabled: false,
percentage: 0,
};
},
created() {},
watch: {
uploadSuccess: {
handler(val) {
if (val) {
this.buttonLoading = false;
this.isDisabled = false;
}
},
deep: true, // 深度监听
immediate: true, // 初次监听即执行
},
},
mounted() {},
methods: {
async confirm() {
if (this.checkMsg && !await this.checkMsg()) {
return false;
}
this.buttonLoading = true;
this.isDisabled = true;
const _self = this;
const maxSize = 200 * 1024 * 1024;
//大于200M文件使用分片上传
if (this.selectFile.size >= maxSize) {
this.upLoadData = this.selectFile.raw;
// 分片上传
uploadByPieces({
file: this.upLoadData, // 文件实体
pieceSize: 20, // 分片大小
baseURL: this.url, //接口地址
selectedSize: 1024, // 用于抽取文件首尾各多少字节作为md5值
success:async (response) => {
this.percentage = 100;
if (response.chunk === response.chunkCount) {
this.$emit("uploadComplete", response);
if (await this.submitMsg(response.filePath, response.chunkFileName)) {
this.buttonLoading = false;
this.isDisabled = false;
} else {
this.buttonLoading = false;
this.isDisabled = false;
}
}
},
uploading: (res) => {
//分片传输中
this.percentage = parseInt((res.current / res.total) * 100);
},
error: (e) => {
this.buttonLoading = false;
this.isDisabled = false;
this.$message({
message: "上传失败!",
type: "success",
});
},
});
} else {
this.submitMsgStep();
}
},
},
};
</script>
comUpload.js
import SparkMD5 from 'spark-md5';
//文件分片处理
export const uploadByPieces = ({
file,
pieceSize = 1,
baseURL,
selectedSize,
success,
uploading,
error
}) => {
const randomNumber = Date.now() + String(Math.round(Math.random() * 1000000)) // 生成随机数
// 上传过程中用到的变量
const chunkSize = pieceSize * 1024 * 1024; // pieceSize:5 MB一片
const chunkCount = Math.ceil(file.size / chunkSize); // 总片数
let identifier = ''
// 计算md5的值
const countMd5 = async () => {
return new Promise(function async(resolve, reject) {
const fileReader = new FileReader()
if (file.size > selectedSize) {
// 文件字节大于分割字节
const md5 = new SparkMD5();
let index = 0;
const loadFile = (start, end) => {
const slice = file.slice(start, end);
fileReader.readAsBinaryString(slice);
}
loadFile(0, selectedSize);
fileReader.onload = e => {
md5.appendBinary(e.target.result);
if (index === 0) {
index += selectedSize;
loadFile(file.size - selectedSize, file.size);
} else {
resolve(md5.end())
}
};
} else {
fileReader.readAsBinaryString(file);
fileReader.onload = e => {
resolve(SparkMD5.hashBinary(e.target.result))
}
}
})
}
// 获取file分片
const getChunkInfo = (file, currentChunk, chunkSize) => {
let start = (currentChunk - 1) * chunkSize;
let end = Math.min(file.size, start + chunkSize);
let chunk = file.slice(start, end);
return { start, end, chunk };
};
// 调用接口上传文件分片
const uploadChunk = chunkInfo => {
let fetchForm = new FormData();
fetchForm.append("taskId", randomNumber); //传输任务ID
fetchForm.append("chunkNumber", chunkInfo.currentChunk); //第几分片
fetchForm.append("chunkSize", chunkSize); //分块的大小
fetchForm.append("totalChunks", chunkInfo.chunkCount); //分片总数
fetchForm.append("identifier", identifier); // 文件唯一标识 md5
fetchForm.append("selectedSize", selectedSize); // 用于抽取文件首尾各多少字节作为md5值
fetchForm.append("originalFileName", file.name); // 用于抽取文件首尾各多少字节作为md5值
fetchForm.append("file", chunkInfo.chunk);// 分块文件传输对象
baseURL(fetchForm).then((res) => {
if (~~res.success) {
if (chunkInfo.currentChunk < chunkInfo.chunkCount) {
const { chunk } = getChunkInfo(
file,
chunkInfo.currentChunk + 1,
chunkSize
);
uploadChunk({
chunk,
currentChunk: chunkInfo.currentChunk + 1,
chunkCount: chunkInfo.chunkCount
});
uploading && uploading({
current: chunkInfo.currentChunk,
total: chunkInfo.chunkCount
});
} else {
// 当总数大于等于分片个数的时候
if (chunkInfo.currentChunk >= chunkInfo.chunkCount - 1) {
res.chunk = chunkInfo.currentChunk;
res.chunkCount = chunkInfo.chunkCount;
success && success(res);
}
}
} else {
error && error(res);
}
})
};
// 针对每个文件进行chunk处理
const readChunk = () => {
// 针对单个文件进行chunk上传
const { chunk } = getChunkInfo(file, 1, chunkSize);
uploadChunk({ chunk, currentChunk: 1, chunkCount });
};
countMd5().then(function (result) {
identifier = result
readChunk(); // 开始执行代码
})
};