纯前端实现视频压缩:WebAssembly + FFmpeg 实战
2026/5/8 15:29:52 网站建设 项目流程

前言

传统在线视频压缩工具需要把文件上传到服务器,用户等待上传 → 服务器处理 → 再下载,流程长、服务器成本高,还存在隐私泄露风险。

本文介绍如何利用 FFmpeg 的 WebAssembly 版本,在浏览器本地实现视频压缩,**文件全程不上传服务器**。

技术选型

- **@ffmpeg/ffmpeg**:FFmpeg 的 WASM 编译版本,在浏览器中运行完整的 FFmpeg 命令行
- **@ffmpeg/util**:工具库,提供 `fetchFile` 等便捷方法
- **SharedArrayBuffer**:WASM 多线程支持,提升编解码性能

核心实现

1. 初始化 FFmpeg 引擎

import { FFmpeg } from '@ffmpeg/ffmpeg' import { fetchFile } from '@ffmpeg/util' const ffmpeg = new FFmpeg() await ffmpeg.load()

FFmpeg WASM 核心文件约 31MB,首次需要从 CDN 加载,之后会被浏览器缓存。

2. 视频信息读取

压缩前需要获取视频的宽高、时长等信息,用于计算压缩参数:

async function getVideoInfo(file: File) { const inputName = 'input' + getExtension(file.name) await ffmpeg.writeFile(inputName, await fetchFile(file)) await ffmpeg.exec(['-i', inputName]) // 从 stderr 中解析视频元数据 }

3. 自适应压缩算法

核心思路:根据目标文件大小反推需要的编码参数。

async function compressToTargetSize(file: File, targetSizeMB: number) { /* 1. 读取视频信息 */ const info = await getVideoInfo(file) /* 2. 根据目标大小计算目标比特率 */ const targetBitrate = Math.floor((targetSizeMB * 8192) / info.duration) /* 3. 根据压缩率动态决定是否缩小分辨率 */ const ratio = targetSizeMB / (file.size / 1024 / 1024) let scaleFilter = '' if (ratio < 0.1) scaleFilter = 'scale=640:360' else if (ratio < 0.4) scaleFilter = 'scale=1280:720' /* 4. 构建 FFmpeg 命令 */ const args = ['-i', inputName, '-c:v', 'libx264'] if (scaleFilter) args.push('-vf', scaleFilter) args.push('-b:v', `${targetBitrate}k`, '-y', outputName) /* 5. 执行压缩 */ await ffmpeg.exec(args) /* 6. 读取结果 */ const data = await ffmpeg.readFile(outputName) return new Blob([data]) }

4. 同等画质优先复制(关键优化)

如果源文件的目标格式与源编码一致,可以直接**复制视频流**,实现秒级处理:

// MP4 → MP4 转换时,尝试直接复制视频流 if (isSameContainer) { await ffmpeg.exec(['-i', inputName, '-c:v', 'copy', '-c:a', 'copy', '-y', outputName]) }

5. 进度追踪

通过监听 FFmpeg 的日志输出,解析处理进度:

ffmpeg.on('progress', ({ progress, time }) => { console.log(`压缩进度: ${(progress * 100).toFixed(1)}%`) })

完整 Composables 封装

将上述逻辑封装为 Vue Composables,方便在组件中复用:

export function useVideoCompress() { const isProcessing = ref(false) const progress = ref({ percent: 0, time: 0 }) const error = ref<string | null>(null) async function compressAuto(file: File): Promise<Blob> { isProcessing.value = true error.value = null // ... 上述核心逻辑 } return { isProcessing, progress, error, compressAuto } }

组件中使用

<script setup> import { useVideoCompress } from './composables/useVideoCompress' const { isProcessing, progress, error, compressAuto } = useVideoCompress() async function handleCompress(file) { const blob = await compressAuto(file) const url = URL.createObjectURL(blob) // 提供给用户下载 } </script>

部署注意

FFmpeg WASM 需要 `SharedArrayBuffer` 支持,服务器响应头需配置:

Cross-Origin-Opener-Policy: same-origin Cross-Origin-Embedder-Policy: require-corp

Vite 开发环境可在 `vite.config.ts` 中配置:

export default defineConfig({ server: { headers: { 'Cross-Origin-Opener-Policy': 'same-origin', 'Cross-Origin-Embedder-Policy': 'require-corp' } } })

总结

纯前端 WASM 方案相比传统服务端方案有几个明显优势:

对比项纯前端 WASM传统服务端
文件上传不上传必须上传
隐私安全文件不离设备存储在第三方
服务器成本
文件大小限制通常有限制

这套方案已在 91AI工具(https://www.91aitool.cn/tools/video-compress-wasm) 生产环境中运行,支持 MP4/WebM/MOV/AVI/MKV 等格式的在线压缩,完全免费使用。

参考链接:
- FFmpeg WASM:https://github.com/ffmpegwasm/ffmpeg.wasm
- 在线体验:https://www.91aitool.cn/tools/video-compress-wasm

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询