版本说明
- "vue": "^3.3.4",
- "element-plus": "^2.3.8"
这里主要使用 el-upload 中的:on-change="handleChange" 就可以实现
代码
<template>
<el-upload :file-list="value"
action="#"
drag
:limit="maxSize"
with-credentials
:multiple="multiple"
:auto-upload="false"
:show-file-list="showFileList"
:on-change="handleChange"
>
<el-icon class="el-icon--upload">
<upload-filled/>
</el-icon>
<div class="el-upload__text">
<em>点击上传</em>或者将文件拖拽到虚线框内上传文件
</div>
</el-upload>
</template>
<script setup>
import {ElMessage, ElMessageBox, ElNotification} from "element-plus";
import {getToken} from '@/utils/auth'
import {getUploadUrl, uploadSingle} from "@/api/home";
const headers = reactive({
authorization: getToken(), //token
'Access-Control-Allow-Origin': "*",
'access-control-allow-headers': 'token, Origin, X-Requested-With, Content-Type, Accept, Authorization',
'access-control-allow-methods': ' POST,GET,PUT,DELETE,HEAD,OPTIONS,PATCH',
'access-control-allow-origin': '*',
'Access-Control-Origin': 'https://xxx.xx.com'
})
const props = defineProps({
value: {
type: Array,
default: () => {
return []
}
},
maxSize: {
type: Number,
default: 1
},
showFileList: {
type: Boolean,
default: false
},
multiple: {
type: Boolean,
default: false
}
})
const handleChange = (file, fileList) => {
handleUpload(fileList)
}
/**
* 判断文件大小, 调取不同接口上传
* @param fileList 文件列表
*/
const handleUpload = (fileList) => {
//如果文件小于 1M, 直接上传
let fd = new FormData();
fileList.forEach(item => {
fd.append("file", item.raw);
})
fd.append("uid", localStorage.getItem('uid'));//接口参数
if (fileList[0].size < chunkRef.value) {
//singleUpload(fd)//小文件直接上传方法
} else {
// 如果文件大于等于 5MB,分片上传
uploadByPieces(fileList[0])
}
}
/**
* 大文件上传接口
* @param data 接口参数
*/
const uploadChunk = (data) => {
return new Promise((resolve, reject) => {
uploadMultiple(data).then(res => {
return resolve(res)
}).catch(error => {
return reject(error)
});
})
}
/**
* 获取文件分块信息
* @param file 文件对象,包含文件的原始信息和原始内容
* @param index 分块索引,用于确定当前是第几个分块
* @returns {{start: number, chunk: *, end: number}}
*/
const getChunkInfo = (file, index) => {
// 上传过程中用到的变量
const chunkSize = chunkRef.value;
//计算当前分块的起始位置
let start = index * chunkSize;
// 计算当前分块的结束位置,确保不超过文件总大小
let end = Math.min(file.size, start + chunkSize);
// 从文件中切分出当前分块的内容
let chunk = file.raw.slice(start, end);
// 返回当前分块的起始位置、结束位置和分块内容
return {start, end, chunk};
};
/**
* 异步处理上传队列中的文件
* @returns {Promise<void>}
*/
async function processUploadQueue() {
// 检查是否有文件正在上传,如果有,则不执行任何操作
if (isUploading) {
return;
}
isUploading = true;
// 当上传队列中有文件时,循环处理队列中的文件
while (uploadQueue.length > 0) {
// 从队列中取出第一个文件的上传信息
const {resolve, reject, fetchForm} = uploadQueue.shift();
try {
const res = await uploadChunk(fetchForm);
console.log(res, 'vs');
if (res.code === 200) {
res.success = true
}
// 假设上传成功
resolve(res.success); // 返回 true 表示成功
} catch (error) {
reject(error);
}
}
isUploading = false;
}
/**
* 读取文件块并添加到上传队列中
* 该函数将给定文件分割成多个块,并将指定索引的块添加到上传队列中
* 它使用异步方式读取文件块,并在读取完成后处理上传逻辑
* @param {File} file - 要上传的文件对象
* @param {number} index - 当前文件块的索引
* @param {number} chunkCount - 文件块的总数
*/
const readChunk = (file, index, chunkCount) => {
return new Promise(async (resolve, reject) => {
const {chunk} = getChunkInfo(file, index);
// 创建 FormData 对象以准备上传的数据
let fetchForm = new FormData();
fetchForm.append("file", chunk);
fetchForm.append("index", index);
fetchForm.append("chunkCount", chunkCount);
fetchForm.append("fileName", file.name);
// 将任务添加到队列中
uploadQueue.push({resolve, reject, fetchForm});
// 如果队列中只有一个任务,则开始处理
processUploadQueue();
});
};
const uploadByPieces = async (file) => {
const chunkCount = Math.ceil(file.size / (chunkRef.value)); // 总片数
// 针对单个文件进行 chunk 上传
// 针对每个文件进行 chunk 处理
const promiseList = []
for (let index = 0; index < chunkCount; ++index) {
promiseList.push(readChunk(file, index, chunkCount))
}
// 使用 Promise.all 等待所有 readChunk 调用完成
Promise.all(promiseList).then((res) => {
// 所有 readChunk 调用完成后,调用 getUploadUrl
if (res.every(item => item === true)) {
ElNotification({
title: '提示',
message: '文件上传成功',
type: 'success'
})
}
}).catch(err => {
console.error('Error reading chunks:', err);
});
}
</script>