You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 08:33:27