Antd中Upload组件封装及使用:

news2025/6/4 0:17:32

1.Upload上传组件功能:

  •   文件校验 :

        文件格式校验/文件大小校验/上传文件总个数校验

  •   相关功能 :

         拖拽功能/上传到远程(七牛)/文件删除及下载

2.组件效果展示:

3.疑难点及解决方案:

  Promise.all多文件并行上传到远程(七牛云):

 (1)在beforeUpload钩子函数中获取token

 (2)循环fileList文件列表,使用fetch将所有文件上传到七牛,并将结果包装成Promise return出去

 (3)待所有文件上传成功后,通过Promise.all获取并存储结果,并通过useEffect及时将七牛返回的结果添加到fileList文件列表中。

 (4)注:由于一次上传多个文件时,beforeUpload钩子函数会执行多次,需要使用debounce进行防抖。

const [promiseAllResult, setPromiseAllResult] = useState<UploadFile[]>([]);

const beforeUpload: UploadProps["beforeUpload"] = (file, fileList) => {
		debouncedBeforeUpload(fileList);
		return false;
};

const debouncedBeforeUpload = debounce(async fileList => {
		...
		const res = await getQiniuTokenApi();
		const uploadPromises = fileList.map(async (file: any) => {
				return new Promise((resolve, reject) => {
					const formData = new FormData();
					formData.append("file", file);
					formData.append("token", res.data?.upToken || "");

					fetch("https://upload.qiniup.com/", {
						method: "POST",
						body: formData
					})
						.then(response => {
							if (response.status === 200) {
								return response
									.json()
									.then(data => {
										// 返回包含文件信息和响应数据的对象
										resolve({
											uid: file.uid,
											url: "https://" + res.data?.domain + "/" + data.key + "?attname=" + file.name,
											filePreviewUrl: "https://" + res.data?.domain + "/" + data.key
										});
									})
									.catch(() => {
										reject(new Error("Upload failed"));
									});
							} else {
								reject(new Error("Upload failed"));
							}
						})
						.catch(error => reject(error));
				});
		});
		Promise.all(uploadPromises)
				.then(res => {
					setPromiseAllResult(res);
					message.success("文件上传成功");
				})
				.catch(() => message.error("文件上传失败"));
});

useEffect(() => {
		if (promiseAllResult && promiseAllResult.length > 0 && fileList && fileList.length > 0) {
				fileList.forEach(item => {
					const findResult = promiseAllResult.find(file => file.uid === item.uid);
					if (findResult) {
						// @ts-ignore
						item.filePreviewUrl = findResult.filePreviewUrl;
						item.url = findResult.url;
					}
				});
		}
}, [promiseAllResult]);

4.完整代码:

  • 封装文件上传组件:

      src/component/Upload/index.tsx:

import { forwardRef, useImperativeHandle, useState, useEffect } from "react";
import { Upload, message } from "antd";
import { InboxOutlined } from "@ant-design/icons";
import type { UploadFile, UploadProps } from "antd";
import { getQiniuTokenApi } from "@/api/modules/assetManagement";
import { debounce } from "lodash";
const { Dragger } = Upload;
interface UploadComType {
	maxCount?: number;
	accept?: string[];
	size?: number;
	multiple?: boolean;
}
const UploadCom = forwardRef(
	(
		{
			maxCount = 3,
			accept = [
				".doc",
				".docx",
				".xml",
				"application/msword",
				"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
				"application/pdf",
				"image/png",
				"image/jpeg"
			],
			size = 2,
			multiple = true
		}: UploadComType,
		ref: any
	) => {
		const [fileList, setFileList] = useState<UploadFile[]>([]);
		const [promiseAllResult, setPromiseAllResult] = useState<UploadFile[]>([]);
		useEffect(() => {
			if (promiseAllResult && promiseAllResult.length > 0 && fileList && fileList.length > 0) {
				fileList.forEach(item => {
					const findResult = promiseAllResult.find(file => file.uid === item.uid);
					if (findResult) {
						// @ts-ignore
						item.filePreviewUrl = findResult.filePreviewUrl;
						item.url = findResult.url;
					}
				});
			}
		}, [promiseAllResult]);
		useImperativeHandle(ref, () => ({
			getFileList: () => {
				return fileList;
			},
			parentSetList: (list: UploadFile[]) => {
				setFileList(list);
			}
		}));
		const onRemove = (file: UploadFile) => {
			const list = fileList.filter(item => item.uid !== file.uid);
			setFileList(list);
		};
		const beforeUpload: UploadProps["beforeUpload"] = (file, fileList) => {
			debouncedBeforeUpload(fileList);
			return false;
		};
		const debouncedBeforeUpload = debounce(async fileList => {
			let newFileList = fileList.filter((file: any) => {
				// 上传中的文件不进行校验
				if (file.status === "uploading") return true;

				// 校验文件类型
				const isFileTypeValid = accept.includes(file.type || "");
				if (!isFileTypeValid) {
					message.error(`${file.name} 不是允许的文件类型`);
					return false;
				}
				// 校验文件大小
				const isFileSizeValid = (file.size || 0) <= size * 1024 * 1024;
				if (!isFileSizeValid) {
					message.error(`${file.name} 超过最大文件大小限制 (${size}MB)`);
					return false;
				}
				return true;
			});
			if (newFileList.length === 0) {
				message.warning("没有符合要求的文件可上传");
				return false;
			}

			const res = await getQiniuTokenApi();
			const uploadPromises = newFileList.map(async (file: any) => {
				return new Promise((resolve, reject) => {
					const formData = new FormData();
					formData.append("file", file);
					formData.append("token", res.data?.upToken || "");

					fetch("https://upload.qiniup.com/", {
						method: "POST",
						body: formData
					})
						.then(response => {
							if (response.status === 200) {
								return response
									.json()
									.then(data => {
										// 返回包含文件信息和响应数据的对象
										resolve({
											name: file.name,
											size: file.size,
											uid: file.uid,
											type: file.type,
											status: file.status,
											url: "https://" + res.data?.domain + "/" + data.key + "?attname=" + file.name,
											filePreviewUrl: "https://" + res.data?.domain + "/" + data.key
										});
									})
									.catch(() => {
										reject(new Error("Upload failed"));
									});
							} else {
								reject(new Error("Upload failed"));
							}
						})
						.catch(error => reject(error));
				});
			});
			Promise.all(uploadPromises)
				.then(res => {
					setPromiseAllResult(res);
					message.success("文件上传成功");
				})
				.catch(() => message.error("文件上传失败"));
		});
		const handleChange: UploadProps["onChange"] = ({ fileList }) => {
			let newFileList = fileList;
			if (newFileList.length > maxCount) {
				message.warning(`最多可上传${maxCount}个文件`);
				newFileList = newFileList.slice(-maxCount);
			}
			setFileList(newFileList);
		};

		const uploadProps: UploadProps = {
			name: "file",
			onRemove,
			beforeUpload: beforeUpload,
			multiple: multiple,
			onChange: handleChange,
			accept: accept.join(",")
		};

		return (
			<div>
				<Dragger {...uploadProps} fileList={fileList}>
					<p className="ant-upload-drag-icon">
						<InboxOutlined />
					</p>
					<p className="ant-upload-text">Click or drag file to this area to upload</p>
					<p className="ant-upload-hint">
						Support for a single or bulk upload. Strictly prohibited from uploading company data or other banned files.
					</p>
				</Dragger>
			</div>
		);
	}
);
export default UploadCom;
  • 使用文件上传组件:
import UploadCom from "@/components/Upload/index";

const uploadComRef = useRef<any>(null);

<UploadCom ref={uploadComRef} />

//获取组件中的文件
const file = uploadComRef.current?.getFileList();

//给组件中的文件赋初始值
uploadComRef.current?.parentSetList(files);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2386621.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux环境基础开发工具->vim

引入&#xff1a;vim是什么&#xff1f; vs叫作继承开发环境&#xff0c;我们可以在里面编辑代码&#xff0c;调式代码&#xff0c;运行代码....这种叫集成开发环境&#xff1b;而vim只用来编辑代码&#xff0c;也就是类似于在windows上打开一个记事本来写代码的操作 集成开发…

跳板问题(贪心算法+细节思考)

首先直接看题&#xff1a; 这题直接贪心其实问题不大&#xff1a; 下面先展示我的一个错误代码&#xff1a; # include<iostream> # include<vector> # include<algorithm>using namespace std;int main() {int N,M;cin>>N>>M;vector<vecto…

RuoYi前后端分离框架集成UEditorPlus富文本编辑器

一、背景 采用若依框架搭建了一个小型的电子书项目,项目前端、后端、移动端就一人,电子书的章节内容是以富文本内容进行呈现的,产品设计人员直接给了一个第三方收费的富文本编辑器截图放到开发文档中,提了一沓需求点,概况下来就是要做成下图中的样子。作为一个后端开发人…

IPD流程落地:项目任务书Charter开发

目录 简介 第一个方面&#xff0c;回答的是Why的问题。 第二点&#xff0c;要回答做什么的问题&#xff0c;也就是产品定义What的问题。 第三点就是要回答执行策略与计划的问题&#xff0c;也就是How、When、Who的问题。 第四点是对上述这些分析的总结分析&#xff0c;要为…

Vue 2 混入 (Mixins) 的详细使用指南

1.基本概念 混入 (Mixins) 是 Vue 2 中用于组件代码复用的重要特性&#xff0c;它允许你将可复用的功能分发到多个组件中。 混入是一种灵活的代码复用方式&#xff0c;可以包含任意组件选项&#xff08;data、methods、生命周期钩子等&#xff09;。当组件使用混入时&#xff…

FreeRTOS 在物联网传感器节点的应用:低功耗实时数据采集与传输方案

FreeRTOS 在物联网传感器节点的应用&#xff1a;低功耗实时数据采集与传输方案 二、FreeRTOS 任务划分与优先级设计 任务名称优先级执行周期功能描述Sensor_Collect3100ms多传感器数据采集与预处理Data_Process2事件驱动数据滤波/压缩/异常检测LoRa_Transmit41s低功耗长距离数…

Linux环境下基于Docker安装 PostgreSQL数据库并配置 pgvector

文章目录 1 docker-compose 安装 PostgreSQL 容器内安装 pgvector1.1 基于 docker-compose 安装 PostgreSQL 数据库1.2 容器内配置 pgvector 2. docker-compose Dockerfile 形式直接配置PostgreSQL数据库及 pgvector参考资料 PostgreSQL是一种功能强大的开源关系数据库管理系…

(9)-java+ selenium->元素定位之By name

1.简介 上一篇已经介绍了通过id来定位元素,继续介绍其他剩下的七种定位方法中的通过name来定位元素。本文来介绍Webdriver中元素定位方法之By name,顾名思义,就是我们想要定位的目标元素节点上,有一个name ="value"的属性,这样我们就可以通过name的value直接去…

深浅拷贝?

一、定义&#xff1a; 浅拷贝&#xff1a;只复制对象的第一层属性&#xff0c;若第一层属性是引用类型&#xff08;如对象、数组&#xff09;&#xff0c;则复制其内存地址&#xff0c;修改拷贝后的嵌套对象会影响原对象。 深拷贝&#xff1a;递归复制对象的所有层级&#xf…

Beckhoff PLC 功能块 FB_CTRL_ACTUAL_VALUE_FILTER (模拟量滤波)

1. 功能块概览 名称&#xff1a;FB_CTRL_ACTUAL_VALUE_FILTER&#xff08;实际值滤波控制功能块&#xff09;。作用&#xff1a;对测量输入值进行合理性检查&#xff08; plausibility check &#xff09;和滤波处理&#xff0c;防止异常跳变&#xff08;如传感器信号突变&…

【C++指南】string(四):编码

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《C指南》 期待您的关注 引言 在 C 编程中&#xff0c;处理字符串是一项极为常见的任务。而理解字符串在底层是如何编码存储的&…

深度学习之序列建模的核心技术:LSTM架构深度解析与优化策略

LSTM深度解析 一、引言 在深度学习领域&#xff0c;循环神经网络&#xff08;RNN&#xff09;在处理序列数据方面具有独特的优势&#xff0c;例如语音识别、自然语言处理等任务。然而&#xff0c;传统的 RNN 在处理长序列数据时面临着严重的梯度消失问题&#xff0c;这使得网…

AI量化交易是什么?它是如何重塑金融世界的?

第一章&#xff1a;证券交易的进化之路 1.1 从喊价到代码&#xff1a;交易方式的革命性转变 在电子交易普及之前&#xff0c;证券交易依赖于交易所内的公开喊价系统。交易员通过手势、喊话甚至身体语言传递买卖信息&#xff0c;这种模式虽然直观&#xff0c;但效率低下且容易…

分布式事务处理方案

1. 使用Seata框架解决 1.1 XA 事务 1.1.1 XA整体流程 第一阶段 RM1开启XA事务-> 执行业务SQL -> 上报TC执行结果RM2开启XA事务-> 执行业务SQL -> 上报TC执行结果 第二阶段 TC根据 RM上报结果通知RM一起提交/回滚XA事务 1.1.2 XA特点 XA 模式必须要有数据库的支…

CVE-2024-36467 Zabbix权限提升

漏洞描述 在Zabbix中&#xff0c;具有API访问权限的已认证用户&#xff08;例如具有默认用户角色的用户&#xff09;可以通过调用user.update API接口&#xff0c;将自己添加到任何用户组&#xff08;如Zabbix管理员组&#xff09;。然而&#xff0c;用户无法添加到已被禁用或…

Dify中的自定义模型插件开发例子:以xinference为例

本文使用Dify v1.0.0-beta.1版本。模型插件结构基本是模型供应商&#xff08;模型公司&#xff0c;比如siliconflow、xinference&#xff09;- 模型分类&#xff08;模型类型&#xff0c;比如llm、rerank、speech2text、text_embedding、tts&#xff09;- 具体模型&#xff08;…

尚硅谷redis7 33-36 redis持久化之RDB优缺点及数据丢失案例

官网说明优点&#xff1a; RDB是Redis数据的一个非常紧凑的单文件时间点表示,RDB文件非常适合备份。例如,您可能希望在最近的24小时内每小时旧档一次RDB文件,并在30天内每天保存一个RDB快照,这使您可以在发生来难时轻松恢复不同版本的数据集。RDB非常适合灾难恢复,它是一个可以…

计算机网络-WebSocket/DNS/Cookie/Session/Token/Jwt/Nginx

文章目录 WebSocketDNS什么是dns域名解析底层协议 cookie/sessionToken/JWTNginx WebSocket 一种网络通信协议&#xff0c;允许在单个 TCP&#xff08;半双工&#xff09; 连接上进行全双工通信&#xff08;客户端和服务器可同时双向传输数据&#xff09;。 HTTP是基于请求-响…

将网页带格式转化为PDF

# 一、安装插件 SingleFile | 将完整的页面保存到一个 HTML 文件中 – 下载 &#x1f98a; Firefox 扩展&#xff08;zh-CN&#xff09; 打开火狐浏览器&#xff0c;安装上面的插件 # 二、下载html单文件 打开对应的网页&#xff0c;点击插件下载对应的html文件 # 三、打开…

【ArcGIS】ArcGIS AI 助手----复现

github地址 korporalK/Archer-GIS-AI-Assitant&#xff1a;Archer 在 ArcGIS Pro 中将自然语言命令转换为自动化 GIS 工作流。它使用代理框架&#xff08;计划-验证-执行&#xff09;构建并由 LLM 提供支持&#xff0c;可简化空间分析、减少手动工作并使 GIS 更易于访问。Arch…