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

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;

核心思路说明

  1. 同步校验前置:利用内置validator快速拦截不符合基础规则的文件(如非视频、过大文件),减少后续异步校验的不必要开销。
  2. 异步校验后置:在onDrop事件中处理异步逻辑,onDrop支持异步函数,可以直接使用await调用getVideoDuration。
  3. 状态可视化:通过fileStatus和fileErrors状态,给用户展示实时的校验进度和结果,提升交互体验。

内容的提问来源于stack exchange,提问作者plutolaser

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 15:25:01