vue2.x+Element 实现大文件切片上传
vue2.x+Element 实现大文件切片上传功能要求效果功能要求支持切片上传;支持并发上传;支持显示上传进度;<template><div><el-row><el-upload:http-request="upload":action="uploadUrl":data="uploadData":before-remove="beforeRemove
·
功能要求
- 支持切片上传;
- 支持并发上传;
- 支持显示上传进度;
<template>
<div>
<el-row>
<el-upload
:http-request="upload"
:action="uploadUrl"
:data="uploadData"
:before-remove="beforeRemove"
name="file"
>
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
</el-row>
<el-row style="margin-top: 50px">
<el-col :span="6">
<el-progress
:text-inside="true"
:stroke-width="18"
:percentage="value"
></el-progress>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
value: 0,
uploadData: {
//这里面放额外携带的参数
},
//文件上传的路径
uploadUrl: "http://127.0.0.1:8980/api/platform/files/mapFile" //文件上传的路径
};
},
methods: {
//错误信息
getError(action, option, xhr) {
const err = new Error("上传错误");
err.status = xhr.status;
err.method = "post";
err.url = action;
return err;
},
// 分片上传的自定义请求,以下请求会覆盖element的默认上传行为
upload(option) {
if (typeof XMLHttpRequest === "undefined") {
return;
}
const fileReader = new FileReader(); // 文件读取类
const action = option.action; // 文件上传上传路径
const chunkSize = 1024 * 1024 * 1; // 单个分片大小,这里测试用1m
const optionFile = option.file; // 需要分片的文件
let fileChunkedList = []; // 文件分片完成之后的数组
const percentage = []; // 文件上传进度的数组,单项就是一个分片的进度
// 文件开始分片,push到fileChunkedList数组中
for (let i = 0; i < optionFile.size; i = i + chunkSize) {
const tmp = optionFile.slice(
i,
Math.min(i + chunkSize, optionFile.size)
);
if (i === 0) {
fileReader.readAsArrayBuffer(tmp);
}
fileChunkedList.push(tmp);
}
// 在文件读取完毕之后,开始计算文件
fileReader.onload = async (e) => {
// 将fileChunkedList转成FormData对象,并加入上传时需要的数据
fileChunkedList = fileChunkedList.map((item, index) => {
const formData = new FormData();
if (option.data) {
Object.keys(option.data).forEach((key) => {
formData.append(key, option.data[key]);
});
// 看后端需要哪些,就传哪些,也可以自己追加额外参数
formData.append(option.filename, item, option.file.name); // 文件
formData.append("chunkNumber", index + 1); // 当前文件数
formData.append("filename", option.file.name); // 文件名
formData.append("totalChunks", fileChunkedList.length); // 总块数
}
return { formData: formData, index: index };
});
// 创建队列上传任务,limit是上传并发数
function sendRequest(chunks, limit = 2) {
return new Promise((resolve, reject) => {
const len = chunks.length;
let counter = 0;
let isStop = false;
const start = async () => {
if (isStop) {
return;
}
const item = chunks.shift();
if (item) {
const xhr = new XMLHttpRequest();
const index = item.index;
// 分片上传失败回调
xhr.onerror = function error(e) {
isStop = true;
reject(e);
};
// 分片上传成功回调
xhr.onload = function onload() {
if (xhr.status !== 200) {
isStop = true;
reject(getError(action, option, xhr));
}
if (counter === len - 1) {
// 最后一个上传完成
resolve();
} else {
counter++;
start();
}
};
// 分片上传中回调
if (xhr.upload) {
xhr.upload.onprogress = (e) => {
percentage[index] = e.loaded;
updataPercentage(e);
};
}
xhr.open("post", action, true);
if (option.withCredentials && "withCredentials" in xhr) {
xhr.withCredentials = true;
}
const headers = option.headers || {};
for (const item in headers) {
if (headers.hasOwnProperty(item) && headers[item] !== null) {
xhr.setRequestHeader(item, headers[item]);
}
}
// 文件开始上传
xhr.send(item.formData);
}
};
while (limit > 0) {
setTimeout(() => {
start();
}, Math.random() * 1000);
limit -= 1;
}
});
}
// 更新上传进度条百分比的方法
const updataPercentage = (e) => {
let loaded = 0; // 当前已经上传文件的总大小
percentage.forEach((item) => {
loaded += item;
});
e.percent = (loaded / optionFile.size) * 100;
if (
this.value > parseFloat(Number(e.percent).toFixed(0)) ||
this.value === parseFloat(Number(e.percent).toFixed(0))
)
return;
this.value = parseFloat(Number(e.percent).toFixed(0));
console.log(this.value);
};
try {
// 调用上传队列方法 等待所有文件上传完成
await sendRequest(fileChunkedList, 1);
// 可以在这通知服务端合并
} catch (error) {
option.onError(error);
}
};
},
beforeRemove(file) {
// 如果正在分片上传, 可用 XMLHttpRequest.abort() 取消分片上传
}
}
};
</script>
<style>
</style>
效果
更多推荐
已为社区贡献2条内容
所有评论(0)