Next.js/React/JS客户端压缩用户上传视频方案问询
视频压缩解决方案(Next.js 环境)
一、客户端方案(优先推荐,适配30秒以内视频)
采用FFmpeg WebAssembly版本(@ffmpeg/ffmpeg),直接在浏览器端完成压缩,无需服务器资源,完全符合你的需求场景。
实现步骤:
- 安装依赖:
npm install @ffmpeg/ffmpeg @ffmpeg/core
- 整合到你的组件中:
import Head from 'next/head' import styles from '../styles/Home.module.css' import { useState, useEffect, useRef } from 'react' import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg' export default function Home() { const [videofile, setVideo] = useState(null) const [viddur, setviddur] = useState("") const [compressedSize, setCompressedSize] = useState("") const [isCompressing, setIsCompressing] = useState(false) const ffmpegRef = useRef(createFFmpeg({ log: true })) useEffect(() => { if (!videofile) return // 获取视频时长逻辑保留 const video = document.createElement('video'); video.preload = 'metadata'; video.onloadedmetadata = function() { window.URL.revokeObjectURL(video.src); setviddur(video.duration.toFixed(2)) } video.src = URL.createObjectURL(videofile) }, [videofile]); const compressvid = async () => { if (!videofile || isCompressing) return setIsCompressing(true) const ffmpeg = ffmpegRef.current // 加载FFmpeg核心(首次加载会下载约20MB的Wasm文件,可做加载提示优化) if (!ffmpeg.isLoaded()) await ffmpeg.load() // 将本地视频写入FFmpeg虚拟文件系统 ffmpeg.FS('writeFile', 'input.mp4', await fetchFile(videofile)) // 执行压缩命令:参数可按需调整,以下为通用压缩配置 await ffmpeg.run( '-i', 'input.mp4', '-b:v', '500k', // 视频码率,数值越小文件越小 '-s', '640x360', // 输出分辨率,匹配原视频比例更佳 '-c:v', 'libx264', // H.264编码保证多平台兼容 '-crf', '28', // 恒定质量参数,18-28区间,值越大压缩率越高 '-preset', 'medium', // 压缩速度/质量平衡,可选ultrafast到veryslow 'output.mp4' ) // 读取压缩后文件并计算大小 const data = ffmpeg.FS('readFile', 'output.mp4') const compressedBlob = new Blob([data.buffer], { type: 'video/mp4' }) setCompressedSize((compressedBlob.size / 1024 / 1024).toFixed(2) + ' MB') // 此处可将compressedBlob上传至Firebase // 示例:const storageRef = ref(storage, `videos/${Date.now()}.mp4`); await uploadBytes(storageRef, compressedBlob) // 清理虚拟文件系统 ffmpeg.FS('unlink', 'input.mp4') ffmpeg.FS('unlink', 'output.mp4') setIsCompressing(false) } return ( <div className={styles.container}> <Head> <title>Video Compressor</title> <meta name="description" content="Video compressor" /> <link rel="icon" href="/favicon.ico" /> </Head> <main className={styles.main}> <h1 className={styles.title}> Video compressor </h1> {videofile && ( <> <p>原视频大小: {(videofile.size / 1024 / 1024).toFixed(2)} MB</p> <p>视频时长: {viddur} 秒</p> <p>压缩后大小: {compressedSize || '未压缩'}</p> </> )} <input className={styles.videoinput} type="file" accept="video/mp4,video/x-m4v,video/*" onChange={(e) => setVideo(e.target.files[0])} /> {videofile && ( <div> <video autoPlay loop src={URL.createObjectURL(videofile)} width="300px" height="300px" /> <button onClick={compressvid} disabled={isCompressing} style={{marginTop: '16px'}}> {isCompressing ? '压缩中...' : '开始压缩'} </button> </div> )} </main> </div> ) }
二、服务端方案(Next.js API路由)
若客户端压缩性能不足,可通过Next.js API路由结合Node.js版FFmpeg处理:
实现步骤:
- 安装依赖:
npm install fluent-ffmpeg @ffmpeg-installer/ffmpeg formidable
- 创建
pages/api/compress-video.js:
import ffmpeg from 'fluent-ffmpeg' import { path as ffmpegPath } from '@ffmpeg-installer/ffmpeg' import { IncomingForm } from 'formidable' import fs from 'fs' import path from 'path' import { promisify } from 'util' const unlink = promisify(fs.unlink) ffmpeg.setFfmpegPath(ffmpegPath) export const config = { api: { bodyParser: false, }, } export default async function handler(req, res) { if (req.method !== 'POST') { return res.status(405).json({ error: 'Method not allowed' }) } // 解析上传的视频文件 const form = await new Promise((resolve, reject) => { const form = new IncomingForm() form.parse(req, (err, fields, files) => { if (err) reject(err) resolve({ fields, files }) }) }) const inputPath = form.files.video.filepath const outputPath = path.join(process.cwd(), `compressed-${Date.now()}.mp4`) try { // 执行压缩命令 await new Promise((resolve, reject) => { ffmpeg(inputPath) .outputOptions([ '-b:v 500k', '-s 640x360', '-c:v libx264', '-crf 28' ]) .save(outputPath) .on('end', resolve) .on('error', reject) }) // 返回压缩后的视频 const compressedBuffer = fs.readFileSync(outputPath) res.setHeader('Content-Type', 'video/mp4') res.send(compressedBuffer) // 清理临时文件 await unlink(inputPath) await unlink(outputPath) } catch (error) { res.status(500).json({ error: error.message }) await unlink(inputPath).catch(() => {}) } }
- 客户端调用API:在
compressvid函数中,将视频文件通过FormData发送到该API路由,接收返回的Blob即可。
三、低成本API方案
若不想维护服务器,可选择以下低成本第三方服务:
- Cloudflare Workers:部署FFmpeg Wasm到Workers,按需调用,免费额度足够处理小视频
- Mux Video:提供视频转码API,免费额度含每月50GB流量,适合小体量应用
- AWS Elemental MediaConvert:新用户12个月免费额度,每月可处理10分钟视频,超出后按分钟计费
内容的提问来源于stack exchange,提问作者vana22
相关产品推荐
相关产品推荐

