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

Antd中Upload组件封装及使用:

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-ignoreitem.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-ignoreitem.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);

相关文章:

  • 矩阵短剧系统:如何用1个后台管理100+小程序?技术解析与实战应用
  • CUDA加速的线性代数求解器库cuSOLVER
  • 基于系统整合的WordPress个性化配置方法深度解析:从需求分析到实现过程
  • LeetCode[222]完全二叉树的节点个数
  • 水库大坝、坝肩混凝土面板变形及岸坡位移多断面多测点安全监测新途径——变焦视觉位移监测仪
  • 【优质会议推荐】2025年遥感与航天航空国际会议(IACRSA 2025)
  • `Release`模式下 编译器优化对 gRPC 远程调用的影响 导致堆栈非法访问
  • leetcode 438. 找到字符串中所有字母异位词
  • Axure项目实战:智慧运输平台后台管理端-运单管理
  • 古诗词鉴赏代码
  • 力扣.H指数力扣.字母异位词力扣.289生命游戏力扣452.用最小数量的箭引爆气球力扣.86分隔链表力扣.轮转数组
  • vue vite textarea标签按下Shift+Enter 换行输入,只按Enter则提交的实现思路
  • MRI学习笔记-表征相似性分析(Representational Similarity Analysis, RSA)
  • RNN神经网络
  • 大模型Pre-Training实战解析:实现Qwen3增量预训练
  • Python实现PDB文件预处理
  • LeetCode 404.左叶子之和的递归求解:终止条件与递归逻辑的深度剖析
  • 中科方德鸳鸯火锅平台使用教程:轻松运行Windows应用!
  • 【竖排繁体识别】如何将竖排繁体图片文字识别转横排繁体,转横排简体导出文本文档,基于WPF和腾讯OCR的实现方案
  • One-shot和Zero-shot的区别以及使用场景
  • 网站优化排名推荐/网络站点推广的方法
  • 网站备案服务/小视频关键词汇总
  • 深圳龙岗疫情最新消息多少例了/seo营销专员
  • 工装公司和家装公司的区别/seo专员工作内容
  • 三亚市住房和城乡建设局/怀化网站seo
  • 导航网站cms/公众号引流推广平台