React 批量上传
这里使用了节流函数,通过上传方法:beforeUpload 触发方法来判断是否全部上传。
```import React, { useState, useRef } from 'react';
import { Upload, Button } from 'antd';
const { INVOICE_SERVER } = process.env;
import debounce from 'lodash/debounce';
import { getToken } from '../../../utils/utils';
import { UploadOutlined } from '@ant-design/icons';
import { request, history } from 'umi';
export default function uploadComponent(props) {
const [btnStatus, setBtnStatus] = useState(false);
const [uploadRes, setuploadRes] = useState({});
const fileLists = useRef();
const handleUpload = debounce(async (value) => {
// 请求数据
console.log('请求数据');
const formData = new FormData();
Promise.all(
fileLists.current.map(async (file) => {
try {
formData.append('excelFile', file);
} catch (error) {}
}),
).then(() => {
formData.append('templateNo', props.templateNo);
request(`${INVOICE_SERVER}/excel/import`, {
method: 'POST',
data: formData,
headers: {
'x-access-token': getToken(),
},
requestType: 'form',
})
.then((response) => {
setBtnStatus(false);
setuploadRes(response)
props.handleChangeUpload(response)
})
.catch((error) => {
setBtnStatus(false);
});
}).catch( e => {
setBtnStatus(false);
});
}, 1500);
const uploadprops = {
action: `${INVOICE_SERVER}/import`,
name: 'excelFile',
method: 'post',
showUploadList: false, // 上传的时候不显示文件以及进度
headers: {
'x-access-token': getToken(),
},
beforeUpload(file, fileList) {
console.log(file, fileList, 'beforeUpload');
fileLists.current = [...fileList]; // 上传的时候不显示文件
setBtnStatus(true);
handleUpload();
return false;
},
onChange({ file }) {
console.log(file, 'file');
},
defaultFileList: [],
multiple: true,
};
return (
<div>
<Upload {...uploadprops}>
<Button type="primary" disabled={btnStatus} icon={<UploadOutlined />}>
导入
</Button>
</Upload>
</div>
);
}
Vue
1. 使用Zip 批量上传
上传zip包,通过前端 jszip 三方包进行解压来统一上传
<el-upload
:file-list="fileList"
:before-upload="beforeUpload"
:on-exceed="handleExceed"
:limit="1"
:show-file-list="true"
>
<template #trigger>
<el-button type="primary">点击上传</el-button>
</template>
<template #tip>
<div class="el-upload__tip">
请上传单个ZIP压缩包
</div>
</template>
<div class="uploaded-file" v-for="file in fileList" :key="file.uid">
{{ file.name }}
</div>
</el-upload>
beforeUpload(file) {
if (!this.form.type) {
ElMessage.error({ message: "请选择类型" });
return false;
}
if (file.type === "application/x-zip-compressed") {
this.btnStatus = true;
// 如果是zip文件格式,那么进行添加
this.fileList.pop();
this.fileList.push(file);
const zip = new JSZip();
let formData = new FormData();
zip
.loadAsync(file, {
decodeFileName: function (bytes) {
return iconv.decode(bytes, "gbk"); // 按中文编码
},
})
.then((contents) => {
// 对于每个文件,你可以调用 file(name) 来获取它的内容
console.log(contents.files, "contents.files");
let arrFn = [];
for (let name in contents.files) {
if (!contents.files[name].dir) {
let promise = contents.files[name].async("blob").then((fileContent) => {
formData.append("files", fileContent, name);
});
arrFn.push(promise);
}
}
Promise.all(arrFn).then(() => {
formData.append("type", this.form.type);
this.uploadFile(formData);
});
})
.catch((error) => {
this.btnStatus = false;
// 处理解压错误
console.error(error);
});
// 阻止 el-upload 组件自动上传文件
return false;
} else {
return ElMessage({ message: "请上传zip格式文件", type: "error" });
}
},
多文件直接上传,每一个文件可以查看进度,并且可以进行排序,可编辑file标题
排序通过 sortable.js
<template>
<div>
<el-drawer
class="upload-drawer"
title="新增剧集"
v-model="drawerVisible"
direction="rtl"
size="50%"
:before-close="handleClose"
>
<el-upload
:disabled="successFile.length !== fileList.length"
v-model:file-list="fileList"
class="upload-demo"
:action="actionUrl"
:headers="headers"
multiple
:before-upload="handleBeforeUpload"
:on-preview="handlePreview"
:on-change="handleAvatarChangeVideo"
:on-exceed="handleExceed"
:show-file-list="false"
accept="video/mp4"
>
<el-button type="primary" :disabled="successFile.length !== fileList.length" class="btn-upload">点击上传</el-button>
<template #tip>
<div v-if="statusRef" class="upload-content">
<div v-for="file in fileList" class="upload-content-item" :key="file.uid">
<div>
<span>标题:</span
><el-input style="width: 200px" v-model="file.newName"></el-input>
<el-button
:disabled="successFile.length !== fileList.length"
class="btn"
type="danger"
@click="handleRemove(file)"
plain
>删除</el-button
>
<el-tooltip
class="box-item"
effect="dark"
content="移动"
placement="right"
>
<el-icon
style="line-height: 32px; height: 32px"
size="20px"
color="#337ecc"
class="draggableClass btn"
><Rank
/></el-icon>
</el-tooltip>
</div>
<div class="progress">
<span>剧集:</span>{{ file.name }}
<el-progress :percentage="file.percentage"></el-progress>
</div>
</div>
</div>
</template>
</el-upload>
<div class="mt-footer">
<el-button class="subBtn" :disabled="fileList.length == 0 || successFile.length !== fileList.length" @click="handleSubmit" type="primary" plain>上传</el-button>
<el-button class="subBtn" @click="handleClose" type="danger" plain>取消</el-button>
</div>
</el-drawer>
</div>
</template>
<script setup lang="ts" name="UploadSketch">
import {
fetchBatchSave
} from '/@/api/sketch/index';
import { defineProps, toRef, ref, defineEmits } from "vue";
import { Session } from "/@/utils/storage";
import Sortable from "sortablejs";
import { ElMessage } from "element-plus";
const props = defineProps({
visible: Boolean,
shortPlayId: Number || String,
});
const drawerVisible = toRef(props, "visible"); // 抽屉是否显示
const fileList = ref([]); // 上传文件列表
const actionUrl = `${import.meta.env.VITE_API_URL}/${props.shortPlayId}`;
const headers = {
"x-access-token": Session.get("token"),
};
const emit = defineEmits(["handleCloseAddPlay"]);
const handleClose = () => {
emit("handleCloseAddPlay");
console.log("close");
};
// 文件上传
/**
* 文件上传前的钩子
* @param file
* @returns
*/
const handleBeforeUpload = (file: any) => {
const isLt1G = file.size / 1024 / 1024 / 1024 < 1;
let isExist = fileList.value.some((item) => item.name === file.name);
if (!isLt1G || isExist) {
ElMessage.error(!isLt1G ? "所选上传文件大小不能超过 1G" : '文件已存在');
const index = fileList.value.findIndex((item) => item.uid === file.uid);
if (index > -1) {
fileList.value.splice(index, 1);
}
return false;
}
};
const handlePreview = (file) => {
console.log("@handlePreiew");
};
// 当超出限制时,执行的钩子函数
const handleExceed = () => {
console.log("@handleExceed", file);
};
const successFile = ref([]); // 这里保存所有上传成功的文件
const handleAvatarChangeVideo = (file: any, fileList: any) => {
if (file.status === "success") {
file.newName = file.name.substring(0, file.name.lastIndexOf("."));
if (file.response.entity) {
successFile.value.push({
name: file.name,
url: file.response.entity,
});
// 判断上传成功的文件和 fileList 是否一样,如果一样, 表示全部上传完成
if (successFile.value.length === fileList.length) {
// 初始化 sortable、赋值、
initSort();
}
}
}
};
const handleRemove = (file: any) => {
fileList.value = fileList.value.filter((item) => item.uid !== file.uid);
successFile.value = fileList.value.filter((item) => item.name !== file.name);
};
const newFileList = ref([]);
const statusRef = ref(true);
const initSort = () => {
const table = document.querySelector(`.upload-content`);
Sortable.create(table, {
group: "shared",
animation: 100,
handle: ".draggableClass",
ghostClass: "ghostClass",
onStart: () => {},
// 结束拖动事件
onEnd: (evt: any) => {
statusRef.value = false;
let oldIndex = evt.oldIndex;
let newIndex = evt.newIndex;
let temp = fileList.value[oldIndex];
if (oldIndex < newIndex) {
//向下移动调整顺序
for (var i = oldIndex; i < newIndex; i++) {
fileList.value[i] = fileList.value[i + 1];
}
} else if (oldIndex > newIndex) {
//向上移动时调整顺序
for (var i = oldIndex; i > newIndex; i--) {
fileList.value[i] = fileList.value[i - 1];
}
}
fileList.value[newIndex] = temp;
statusRef.value = true;
initSort();
},
});
};
const handleSubmit = () => {
console.log("handleSubmit", fileList.value);
let addVOS = fileList.value.map((item: any) => {
return {
name: item.newName,
tencentOriginalUrl: item.response.entity,
};
});
let data = {
shortPlayId: props.shortPlayId,
addVOS: addVOS
}
fetchBatchSave(data).then( res => {
if (res.data.status === 200 && res.data.entity) {
ElMessage.success('新增成功');
handleClose()
}
})
};
</script>
<style lang="scss" scoped>
.btn-upload {
margin: 6px 10px;
}
.upload-content {
display: block;
padding: 20px;
margin-bottom: 60px;
.upload-content-item {
margin-top: 10px;
padding: 10px 20px;
width: 100%;
height: 100px;
border: 1px solid #ccc;
}
.btn {
margin-left: 10px;
float: right;
}
.progress {
margin-top: 10px;
}
}
.draggableClass {
cursor: pointer;
}
.upload-drawer {
position: relative;
.mt-footer {
position: absolute;
margin-top: 60px;
width: 100%;
bottom: 0;
height: 50px;
line-height: 50px;
margin-right: 20px;
background-color: #eee;
.subBtn {
float: right;
margin-top: 9px;
margin-right: 20px;
}
}
}
</style>
<style>
.ghostClass {
background-color: #409eff !important;
}
</style>