accept和type无法校验音视频时长,因其仅过滤类型而非元数据;须用video元素加载临时URL并监听loadedmetadata事件异步获取duration,且服务端必须复核。

为什么 accept 和 type 不能校验时长
浏览器原生的 accept 属性只过滤文件类型(MIME)或扩展名,required 只检查是否选中,它们完全不感知音视频的元数据(如时长)。用户选一个 5 小时的 MP4 文件,accept="video/mp4" 一样放行——这不是 bug,是设计如此。
用 FileReader + URL.createObjectURL 读取时长需注意兼容性
真正可行的方式是:选中文件后,用 URL.createObjectURL(file) 创建临时 URL,再用 <video> 或 <audio> 元素加载它,监听 loadedmetadata 事件读取 duration。但要注意:
- 某些老旧 Android WebView 不触发
loadedmetadata,得 fallback 到canplay或加 timeout -
duration在未完全加载时可能为NaN,必须等状态变为readyState >= 2 - 记得调用
URL.revokeObjectURL()清理内存,否则可能堆积大量 blob 引用 - 不要在
change回调里直接console.log(video.duration)——它几乎总是NaN,因为还没加载完
实际校验逻辑要分三步走
完整流程不是“一步读 duration”,而是:
- 先用
file.type和file.name做基础类型过滤(比如只允许video/mp4、audio/mpeg) - 再创建
<video>元素,src设为URL.createObjectURL(file),监听loadedmetadata - 拿到
duration后立刻比对阈值(如 ≤ 60),通过则保留文件、启用提交按钮;失败则input.value = ''清空,并调用setCustomValidity('视频不得超过 60 秒')
关键点:这个过程是异步的,不能阻塞表单提交,必须把提交逻辑挂到校验完成之后。
立即学习“前端免费学习笔记(深入)”;
服务端必须重复校验,前端时长校验只是体验优化
用户可以跳过 JS、伪造 duration、甚至直接发 POST 请求绕过所有前端逻辑。所以:
- 后端收到文件后,必须用 FFmpeg、
ffprobe或语言原生库(如 Python 的moviepy、Node.js 的fluent-ffmpeg)重新提取真实时长 - 前后端阈值必须一致(比如都设为 60 秒),否则用户会看到“前端说 OK,后端报错”的矛盾提示
- 别依赖前端传来的时长字段(如额外加个
durationhidden input)——它不可信
前端校验唯一作用是让用户早一点知道错了,而不是替后端把关。



















