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

Next.js/React/JS客户端压缩用户上传视频方案问询

视频压缩解决方案(Next.js 环境)

一、客户端方案(优先推荐,适配30秒以内视频)

采用FFmpeg WebAssembly版本(@ffmpeg/ffmpeg),直接在浏览器端完成压缩,无需服务器资源,完全符合你的需求场景。

实现步骤:

  1. 安装依赖:
npm install @ffmpeg/ffmpeg @ffmpeg/core
  1. 整合到你的组件中:
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处理:

实现步骤:

  1. 安装依赖:
npm install fluent-ffmpeg @ffmpeg-installer/ffmpeg formidable
  1. 创建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(() => {})
  }
}
  1. 客户端调用API:在compressvid函数中,将视频文件通过FormData发送到该API路由,接收返回的Blob即可。

三、低成本API方案

若不想维护服务器,可选择以下低成本第三方服务:

  • Cloudflare Workers:部署FFmpeg Wasm到Workers,按需调用,免费额度足够处理小视频
  • Mux Video:提供视频转码API,免费额度含每月50GB流量,适合小体量应用
  • AWS Elemental MediaConvert:新用户12个月免费额度,每月可处理10分钟视频,超出后按分钟计费

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 04:20:33