Next.js+TS项目中使用React-Dropzone如何拒绝超大文件上传
报错根因
你当前的报错来自两个逻辑遗漏:
- 配置
maxSize拦截规则后,超大文件不会进入acceptedFiles列表,此时acceptedFiles是空数组,你直接取acceptedFiles[0]执行上传逻辑,会触发空值访问错误。 - 没有实现拒绝文件的处理分支,文件被规则拦截后没有对应的提示和逻辑兜底。
正确实现方案
react-dropzone的onDropRejected会在文件不符合配置规则(大小超限、格式不匹配、数量超限)时触发,你可以在这个回调里统一处理拦截提示,同时补全接受文件分支的边界判断即可。
具体修改点:
- 新增错误提示状态,存储拦截后的文案
- 实现
onDropRejected回调,解析被拒绝文件的错误类型,返回对应提示 - 补全
handleDrop的边界判断,acceptedFiles为空时直接终止逻辑,避免空值报错 - 在UI区域补充错误提示的展示位置
修改后的完整代码如下:
import { useState, useEffect, useCallback } from 'react'; import Dropzone from 'react-dropzone'; import classNames from 'classnames'; // 保留你项目中原有的Icon、AssetApis、AssetResourceType等导入 type Props = { name?: string; isError?: boolean; onChange?: (id: string) => void; }; // 抽离上传限制常量,方便后续维护 const MAX_FILE_SIZE = 30000000; const ACCEPT_FILE_TYPES = ['image/*', 'text/csv']; export const FileUploader = ({ name, isError, onChange }: Props) => { const [uploading, setUploading] = useState(false); const [nameFile, setNameFile] = useState<string>('File format: CSV/Image, Maximum upload size: 30MB'); const [currId, setCurrId] = useState(''); // 新增错误提示状态 const [errorTip, setErrorTip] = useState(''); useEffect(() => { name && setNameFile(name); }, [name]); // 处理不符合规则的被拒绝文件 const handleDropRejected = useCallback((rejectedFiles: Array<{file: File, errors: Array<{code: string, message: string}>}>) => { setErrorTip(''); const firstRejectFile = rejectedFiles[0]; if (!firstRejectFile) return; // 根据错误码返回对应提示文案 const firstError = firstRejectFile.errors[0]; switch (firstError.code) { case 'file-too-large': setErrorTip('文件大小超过30MB限制,请重新选择文件'); break; case 'file-invalid-type': setErrorTip('不支持当前文件格式,仅支持CSV和图片文件'); break; default: setErrorTip('文件不符合上传要求,请重新选择'); } }, []); const handleDrop = useCallback(async (acceptedFiles: File[]) => { setErrorTip(''); // 边界判断:无符合要求的文件直接终止逻辑,避免空值报错 if (acceptedFiles.length === 0) return; setUploading(true); const file = acceptedFiles[0]; try { const res = await AssetApis.create(file, AssetResourceType.marketingAction); if (res.id) { setNameFile(file.name); onChange?.(res.id); currId && (await AssetApis.remove(currId)); setCurrId(res.id); } } catch (err) { setErrorTip('文件上传失败,请重试'); } finally { setUploading(false); } }, [currId, onChange]); return ( <div> <Dropzone onDrop={handleDrop} onDropRejected={handleDropRejected} multiple={false} accept={ACCEPT_FILE_TYPES} disabled={uploading} maxSize={MAX_FILE_SIZE}> {({ getRootProps, getInputProps }) => ( <div {...getRootProps({ className: 'dropzone' })} className='flex items-center w-full h-40 text-center'> <input {...getInputProps()} /> <div className={classNames( 'rounded flex h-full items-center justify-center flex-col flex-1 border border-dashed text-gray-700 mr-2.5 py-6', isError || errorTip ? 'border-danger' : 'border-gray' )}> <Icon name='upload' size={20} /> <div className='mb-2.5 text-medium'>Drag and drop to upload</div> <button type='button' className='px-2.5 border-gray-700 border rounded-full text-small px-3 py-0.5'> Select file </button> <div className='mt-2 text-gray-500 text-small'>{nameFile}</div> {/* 错误提示展示位 */} {errorTip && <div className='mt-1 text-danger text-small'>{errorTip}</div>} </div> </div> )} </Dropzone> </div> ); };
补充说明
- 如果你使用的是v14及以上版本的react-dropzone,
accept属性需要传入MIME类型映射对象格式,旧版本的数组写法会触发警告,可根据实际安装版本调整。 - 回调函数建议用
useCallback包裹,避免子组件不必要的重渲染。 - 上传逻辑增加了
try/catch/finally包裹,避免接口报错时loading状态一直卡住无法重置。
内容的提问来源于stack exchange,提问作者KYin
相关产品推荐
相关产品推荐

