当前位置: 首页 > news >正文

[React]Antd Upload组件上传多个文件

前言

实现需求:上传多个文件。其实就是获取多个文件的绝对路径交给后端接口去处理。

Upload组件

首先,这里不能调用Upload的onChange函数,因为上传中、完成、失败都会调用这个函数,在多个文件的情况下会出现多次调用问题。改用beforeUpload和customRequest。

import React, { useRef, useState, useEffect } from 'react';
import { UploadOutlined } from '@ant-design/icons';
import type { UploadFile, UploadProps } from 'antd';
import { Button, Upload } from 'antd';
import { useTranslation } from 'react-i18next';
import Console from '../../../../util/Console';
import { UploadChangeParam } from 'antd/es/upload';
import { sendMsgToMain } from 'electron-prokit';type Props = {name?: string;onChangeFilePath: (path: string | null | unknown, info?: UploadChangeParam<UploadFile<any>>) => void;accept: Array<string>;headers?: any;progress?: any;buttonTitle?: string;rest?: UploadProps;action?: string | ((file: any) => Promise<string>);
};const FileMultiSelectButton: React.FC<Props> = ({name,onChangeFilePath,accept,headers,progress = null,buttonTitle,action,...rest
}) => {const { t } = useTranslation();const fileState: any = useRef();const [uploadFiles, setUploadFiles] = useState<UploadFile[]>([]);const updateFiles = (function () {let fileList: UploadFile[] | null = null;return function (list: UploadFile[], setState: React.Dispatch<React.SetStateAction<UploadFile[]>>) {if (!fileList) {fileList = list;setState && setState(list);}return {fileList,reset() {fileList = null;}};};})();const beforeUpload = (_: any, fileList: UploadFile[]) => {fileState.current = updateFiles(fileList, setUploadFiles);return false;}const customRequest = () => {if (uploadFiles.length > 0) {const path = uploadFiles.map(item => (item as any).path);// 这是electron的ipc处理函数,在node中复制文件sendMsgToMain({ key: 'fileMultiSelectPath', data: path }).then(filePath => {if (typeof onChangeFilePath === 'function') {onChangeFilePath(filePath);} else {Console.handleErrorMsg('onChangeFilePath is not a function');}});} else {Console.handleWarningMsg('no file uploaded');}}useEffect(() => {if (uploadFiles.length > 0) {customRequest();fileState.current.reset();}}, [uploadFiles]);return (<Uploadname={name || 'file'}accept={accept.join(',')}progress={progress}showUploadList={false}headers={headers}action={action}multiple={true}beforeUpload={beforeUpload}customRequest={customRequest}{...rest}><Button icon={<UploadOutlined />}> {buttonTitle || t('Select')} </Button></Upload>);
};export default FileMultiSelectButton;

在node中处理(不必须),这里主要因为electron需要兼容不同的平台,需要处理文件路径

function generateRandomString(length: number) {let result = '';const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';const charactersLength = characters.length;for (let i = 0; i < length; i++) {result += characters.charAt(Math.floor(Math.random() * charactersLength));}return result;
}
const fileSelectPath = (sourcePath: string) => {return new Promise((resolve, reject) => {const userDataPath = app.getPath('userData');// 获取用户数据目录,并在该目录下创建文件夹,用于保存用户数据,需要判断是否存在,如果存在则直接使用,否则创建,并返回该目录,用于后续操作,并需要兼容跨平台,比如windows和mac// 拼接出 files 目录的完整路径const filesDirPath = path.join(userDataPath, 'files');fs.mkdir(filesDirPath, { recursive: true }, err => {if (err) {// 如果目录创建失败,返回错误信息// 如果目录创建失败,处理错误console.error('Failed to create files directory:', err);}// 生成一个随机数const randomBytes = generateRandomString(8); // 生成8字节的随机数// 获取当前日期和时间const date = new Date();const formattedDate = date.toISOString().split('T')[0].replace(/-/g, ''); // 格式化为不含破折号的日期const formattedTime = date.toTimeString().split(' ')[0].replace(/:/g, ''); // 格式化为不含冒号的时间// 提取源文件名和扩展名const { name, ext } = path.parse(sourcePath);// 根据日期、时间和随机数生成一个新的文件名const newName = `${name}_${formattedDate}_${formattedTime}_${randomBytes}${ext}`;// 构造目标路径const destinationPath = path.join(filesDirPath, newName);// 使用fs模块的copy方法复制文件fs.copyFile(sourcePath, destinationPath, err => {if (err) {// 如果文件已存在或复制失败,返回错误信息reject(err);} else {// 复制成功,返回新的文件路径resolve(destinationPath);}});});});
};export const fileMultiSelectPath = (sourcePath: Array<string>) => {return Promise.all(sourcePath.map(async (sourcePath) => {return await fileSelectPath(sourcePath);}));
};
http://www.dtcms.com/a/349493.html

相关文章:

  • 阿里云安装postgre数据库
  • Vim 的 :term命令:终端集成的终极指南
  • 中介者模式及优化
  • Flink 状态 RocksDBListState(写入时的Merge优化)
  • 元宇宙与个人生活:重构日常体验的数字新维度
  • 技术攻坚与安全兜底——消防智能仓储立库管理系统的国产化硬核实力
  • ADB 调试工具的学习[特殊字符]
  • 性能优化:首屏加载速度的优化
  • Seaborn数据可视化实战:Seaborn高级使用与性能优化教程
  • C++编译链接与性能优化答案
  • 新手入门GEO优化指南:从0到1掌握生成式引擎优化
  • 我们为你连接网络,安装驱动程序
  • 构建AI智能体:十三、大数据下的“搭积木”:N-Gram 如何实现更智能的语义搜索
  • 60 C++ 现代C++编程艺术9-function用法
  • 29.深浅拷贝
  • 用DeepSeek实现实时语音翻译,我们在应用端需要做什么?
  • ssl笔记
  • Rust爬虫实战:用reqwest+select打造高效网页抓取工具
  • 通信中间件 Fast DDS(二) :详细介绍
  • 达梦 manager启动报错
  • COREDUMP
  • vsftp 传着传着速率变慢或者没有了
  • Django时区处理
  • Linux / 宝塔面板下 PHP OPcache 完整实践指南
  • MCP之weather server demo
  • TCP与HTTP协议以及爬虫
  • 计算机毕业设计 java 药店药品信息管理系统 基于 Java 的药店药品管理平台Java 开发的药品信息系统
  • 解析电商本地生活竞争:从我店模式创新到生态协同的进化路径
  • AR智能巡检:市政设施管理的变革力量
  • OpenAI o1:OpenAI最新推出的AI大语言模型,更擅长推理也更贵