TypeScript客户端预获取音视频时长:仅MP4有效求兼容方案
嘿,这个问题我之前做项目时也踩过坑!用<video>标签直接取duration确实只能处理浏览器原生支持的编码格式(比如MP4的H.264编码),碰到MOV、FLV或者一些小众音频格式就歇菜了。下面给你分享几个靠谱的解决方案,覆盖不同场景需求:
方法一:针对音频文件的AudioContext方案
如果你的需求里包含大量音频文件(MP3、WAV、OGG等),可以用浏览器的AudioContext来解析,它对音频格式的支持比<audio>标签更全面,而且能拿到精确的时长:
async function getAudioDuration(file: File): Promise<number> { return new Promise((resolve, reject) => { // 兼容老版浏览器的前缀 const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)(); const fileReader = new FileReader(); fileReader.onload = (event) => { audioContext.decodeAudioData(event.target?.result as ArrayBuffer) .then(audioBuffer => { resolve(audioBuffer.duration); audioContext.close(); // 及时关闭上下文,释放资源 }) .catch(err => { reject(new Error(`解析音频失败:${err.message}`)); audioContext.close(); }); }; fileReader.onerror = () => reject(new Error('读取音频文件时发生错误')); fileReader.readAsArrayBuffer(file); }); }
方法二:兼容音视频元素的事件监听方案
你之前的代码问题大概率是没等媒体元数据加载完成就取了duration!浏览器需要先加载媒体的元数据才能拿到时长,而且针对音频文件应该用<audio>标签,而不是硬套<video>。这个方法能覆盖浏览器原生支持的所有音视频格式:
async function getMediaDuration(file: File): Promise<number> { return new Promise((resolve, reject) => { let mediaElement: HTMLVideoElement | HTMLAudioElement; // 根据文件MIME类型选择对应元素 if (file.type.startsWith('video/')) { mediaElement = document.createElement('video'); } else if (file.type.startsWith('audio/')) { mediaElement = document.createElement('audio'); } else { reject(new Error('仅支持音视频文件')); return; } mediaElement.preload = 'metadata'; // 只加载元数据,不下载整个文件 const objectURL = URL.createObjectURL(file); mediaElement.src = objectURL; // 监听元数据加载完成事件 mediaElement.onloadedmetadata = () => { const duration = mediaElement.duration; URL.revokeObjectURL(objectURL); // 释放URL对象,避免内存泄漏 resolve(duration); }; // 处理格式不支持的情况 mediaElement.onerror = () => { URL.revokeObjectURL(objectURL); reject(new Error(`浏览器不支持解析${file.type}格式的媒体文件`)); }; }); }
方法三:FFmpeg.wasm 全格式解析(终极方案)
如果需要支持浏览器原生不兼容的格式(比如MKV、FLV、MOV等),那就得上FFmpeg.wasm了——它在客户端用WebAssembly运行FFmpeg,几乎能解析所有主流音视频格式,缺点是包体积稍大,适合对格式支持要求高的场景:
首先安装依赖:
npm install @ffmpeg/ffmpeg @ffmpeg/core
然后编写TypeScript代码:
import { FFmpeg } from '@ffmpeg/ffmpeg'; async function getFFmpegMediaDuration(file: File): Promise<number> { const ffmpeg = new FFmpeg(); // 加载FFmpeg核心,建议把core文件下载到本地部署,提升加载速度 await ffmpeg.load({ coreURL: 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/ffmpeg-core.js', }); // 将用户上传的文件写入FFmpeg的虚拟文件系统 await ffmpeg.writeFile(file.name, new Uint8Array(await file.arrayBuffer())); // 执行ffprobe命令提取时长信息 const execOutput = await ffmpeg.exec([ '-v', 'error', '-show_entries', 'format=duration', '-of', 'default=noprint_wrappers=1:nokey=1', file.name ]); // 解析输出的时长字符串,转成数字 const durationStr = execOutput.join('').trim(); const duration = parseFloat(durationStr); if (isNaN(duration)) { throw new Error('无法解析该媒体文件的时长'); } // 清理资源 await ffmpeg.deleteFile(file.name); ffmpeg.terminate(); return duration; }
场景选择建议
- 只需处理浏览器原生支持的音视频格式:用方法二,简单高效,无额外依赖;
- 重点处理音频文件:结合方法一+方法二,覆盖更多音频编码;
- 需要全格式支持:用方法三,虽然有包体积成本,但能解决绝大多数格式兼容问题。
内容的提问来源于stack exchange,提问作者Chinna M
相关产品推荐
相关产品推荐

