如何用Zod异步验证视频文件时长?JavaScript问题求助
解决Zod验证视频时长时等待元数据加载的问题
核心方案:将视频元数据加载包装为Promise配合async/await
要等待onloadedmetadata事件完成后再返回校验结果,关键是把这个事件回调包装成Promise,这样就能在Zod的自定义校验中使用async/await。
第一步:封装获取视频时长的异步函数
function getVideoDuration(file) { return new Promise((resolve, reject) => { const video = document.createElement('video'); video.preload = 'metadata'; // 监听元数据加载完成事件 video.onloadedmetadata = () => { // 加载完成后清理临时URL,避免内存泄漏 URL.revokeObjectURL(video.src); resolve(video.duration); }; // 处理加载错误 video.onerror = () => { URL.revokeObjectURL(video.src); reject(new Error('无法加载视频元数据')); }; // 生成视频的临时访问URL video.src = URL.createObjectURL(file); }); }
第二步:在Zod Schema中使用异步校验
在Zod的refine方法中开启async: true标识,调用上述异步函数完成时长校验:
import { z } from 'zod'; const videoSchema = z.object({ videoFile: z.instanceof(File).refine(async (file) => { const duration = await getVideoDuration(file); // 替换为你的时长规则,比如要求时长大于10秒 return duration > 10; }, { message: '视频时长必须超过10秒', async: true // 必须开启该标识以支持异步校验 }) });
使用该Schema时,需调用异步校验方法safeParseAsync而非同步的safeParse:
// 校验示例 const result = await videoSchema.safeParseAsync({ videoFile: selectedFile }); if (result.success) { // 校验通过后的逻辑 } else { // 处理校验错误 }
你提到的替代方案:提前加载元数据再校验
这种方案将元数据加载与Zod校验分离,先在输入框onChange事件中获取视频时长,再用Zod校验数字类型的时长值。
示例代码(React环境)
import { useState } from 'react'; import { z } from 'zod'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; // 定义校验数字时长的Zod Schema const videoSchema = z.object({ videoDuration: z.number().min(10, '视频时长必须超过10秒') }); export default function VideoUploadForm() { const [videoDuration, setVideoDuration] = useState(null); const { register, handleSubmit, formState: { errors } } = useForm({ resolver: zodResolver(videoSchema) }); const handleFileChange = async (e) => { const file = e.target.files[0]; if (!file) return; try { const duration = await getVideoDuration(file); setVideoDuration(duration); } catch (err) { console.error(err); setVideoDuration(null); } }; const onSubmit = (data) => { // 表单提交逻辑,data.videoDuration为已校验的时长 console.log('提交成功', data); }; return ( <form onSubmit={handleSubmit(onSubmit)}> <input type="file" accept="video/*" onChange={handleFileChange} /> {/* 隐藏输入框传递时长给表单校验 */} <input type="hidden" {...register('videoDuration')} value={videoDuration || ''} /> {errors.videoDuration && <p>{errors.videoDuration.message}</p>} <button type="submit" disabled={videoDuration === null}>提交</button> </form> ); } // 复用之前的getVideoDuration函数 function getVideoDuration(file) { return new Promise((resolve, reject) => { const video = document.createElement('video'); video.preload = 'metadata'; video.onloadedmetadata = () => { URL.revokeObjectURL(video.src); resolve(video.duration); }; video.onerror = () => { URL.revokeObjectURL(video.src); reject(new Error('无法加载视频元数据')); }; video.src = URL.createObjectURL(file); }); }
该方案的优势是把异步操作提前到用户选择文件时完成,后续Zod校验为同步操作,整体体验更流畅。
内容的提问来源于stack exchange,提问作者Ettur
相关产品推荐
相关产品推荐

