React Dropzone + TS:同步校验函数中执行异步视频时长校验方案
解决React Dropzone同步校验器中处理异步视频时长校验的问题
React Dropzone的内置validator函数要求同步执行,无法直接在其中使用await调用异步的视频时长获取方法。这里提供一种直接可行的解决方案:拆分同步与异步校验逻辑,用内置validator处理同步可完成的校验(如文件大小、类型),将异步的时长校验移至onDrop事件中处理。
步骤1:完善异步获取视频时长的函数(修复内存泄漏)
先优化你的getVideoDuration函数,添加错误处理并释放创建的Object URL,避免内存泄漏:
async function getVideoDuration(videoFile: File | Blob) { return new Promise<number>((res, rej) => { const video = document.createElement("video"); video.preload = "metadata"; video.onloadeddata = function () { res(video.duration); URL.revokeObjectURL(video.src); // 释放URL资源 }; video.onerror = function () { rej(new Error("无法加载视频文件,可能文件损坏")); URL.revokeObjectURL(video.src); }; video.src = URL.createObjectURL(videoFile); }); }
步骤2:配置React Dropzone,拆分校验逻辑
在组件中使用useDropzone时,保留内置validator处理同步校验,将异步时长校验放到onDrop事件中,并通过状态管理错误信息:
import { useState } from "react"; import { useDropzone } from "react-dropzone"; function VideoUploader() { // 存储文件校验错误信息 const [fileErrors, setFileErrors] = useState<Record<string, string>>({}); // 存储文件校验状态(加载中/成功/失败) const [fileStatus, setFileStatus] = useState<Record<string, "loading" | "success" | "error">>({}); const { getRootProps, getInputProps, acceptedFiles } = useDropzone({ // 同步校验:处理文件大小、类型等可同步判断的规则 validator: (f) => { let file: File | null; if (f instanceof DataTransferItem) { file = f.getAsFile(); } else { file = f as File; } if (!file) { return { message: "请上传有效文件", code: "InvalidDragObject" }; } // 校验文件大小(500MB) if (file.size > 5e8) { return { message: "文件过大,最大支持500MB", code: "FileTooLarge" }; } // 校验文件类型为视频 if (!file.type.startsWith("video/")) { return { message: "请上传视频格式文件", code: "InvalidFileType" }; } return null; }, // 异步校验:处理视频时长 onDrop: async (acceptedFiles) => { const newErrors: Record<string, string> = {}; const newStatus: Record<string, "loading" | "success" | "error"> = {}; for (const file of acceptedFiles) { newStatus[file.name] = "loading"; setFileStatus(prev => ({ ...prev, ...newStatus })); try { const duration = await getVideoDuration(file); // 校验时长不超过15分钟(900秒) if (duration > 15 * 60) { newErrors[file.name] = "视频时长不能超过15分钟"; newStatus[file.name] = "error"; } else { newStatus[file.name] = "success"; } } catch (err) { newErrors[file.name] = (err as Error).message; newStatus[file.name] = "error"; } } setFileErrors(newErrors); setFileStatus(prev => ({ ...prev, ...newStatus })); // 若所有文件校验通过,执行上传逻辑 if (Object.keys(newErrors).length === 0) { console.log("开始上传文件", acceptedFiles); // 这里添加你的文件上传API调用 } }, }); return ( <div {...getRootProps({ className: "dropzone" })}> <input {...getInputProps()} /> <p>拖拽视频文件至此处,或点击选择文件</p> {/* 渲染已选择文件及校验状态 */} <div className="file-list"> {acceptedFiles.map(file => ( <div key={file.name} className="file-item"> <span>{file.name}</span> {fileStatus[file.name] === "loading" && <span style={{ marginLeft: 8, color: "#666" }}>校验中...</span>} {fileStatus[file.name] === "success" && <span style={{ marginLeft: 8, color: "green" }}>✓ 校验通过</span>} {fileStatus[file.name] === "error" && <span style={{ marginLeft: 8, color: "red" }}>✗ {fileErrors[file.name]}</span>} </div> ))} </div> </div> ); } export default VideoUploader;
核心思路说明
- 同步校验前置:利用内置
validator快速拦截不符合基础规则的文件(如非视频、过大文件),减少后续异步校验的不必要开销。 - 异步校验后置:在
onDrop事件中处理异步逻辑,onDrop支持异步函数,可以直接使用await调用getVideoDuration。 - 状态可视化:通过
fileStatus和fileErrors状态,给用户展示实时的校验进度和结果,提升交互体验。
内容的提问来源于stack exchange,提问作者plutolaser
相关产品推荐
相关产品推荐

