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

React客户端上传前视频压缩方案故障排查求助

React视频压缩功能修复方案

问题分析

你的代码存在以下核心问题导致压缩功能失效:

  • video.onloadedmetadata事件绑定错误,错误赋值为Promise,且未等待视频加载完成就执行后续逻辑
  • 依赖useState的mediaStream获取流,但useState是异步更新的,导致后续代码拿到的是null
  • mediaRecorder.ondataavailable事件绑定错误,用Promise替代了事件回调,无法正确捕获数据块
  • 未触发MediaRecorder.stop(),导致录制过程无法结束,无法生成完整的压缩视频Blob

修复后的完整代码

import React, { useState, useRef } from 'react'

function Test2() {
    const [file, setFile] = useState(null)
    const [compressedFile, setCompressedFile] = useState(null)
    const videoRef = useRef(null)

    const handleFileSelection = (event) => {
        setFile(event.target.files[0])
    }

    const handleCompression = async () => {
        if (!file) return
        
        const videoUrl = URL.createObjectURL(file)
        const video = document.createElement('video')
        
        // 配置视频属性,绕过浏览器自动播放限制
        video.src = videoUrl
        video.muted = true
        video.playsInline = true

        // 等待视频元数据加载完成
        await new Promise((resolve) => {
            video.onloadedmetadata = resolve
        })

        // 开始播放视频才能捕获流
        await video.play()

        // 捕获视频流,设置目标帧率
        const stream = video.captureStream(25)
        // 配置MediaRecorder,指定格式与压缩比特率
        const mediaRecorder = new MediaRecorder(stream, {
            mimeType: 'video/mp4',
            videoBitsPerSecond: 500000 // 500kbps,可按需调整数值控制压缩程度
        })

        const chunks = []

        // 正确绑定数据捕获事件
        mediaRecorder.ondataavailable = (event) => {
            if (event.data.size > 0) {
                chunks.push(event.data)
            }
        }

        // 录制完成后生成压缩文件
        mediaRecorder.onstop = () => {
            const recordedBlob = new Blob(chunks, { type: 'video/mp4' })
            setCompressedFile(URL.createObjectURL(recordedBlob))
            // 清理临时URL,避免内存泄漏
            URL.revokeObjectURL(videoUrl)
        }

        // 启动录制
        mediaRecorder.start()

        // 视频播放结束后停止录制并清理流
        video.onended = () => {
            mediaRecorder.stop()
            stream.getTracks().forEach(track => track.stop())
        }
    }

    return (
        <>
            <input type="file" accept="video/*" onChange={handleFileSelection} />
            <button onClick={handleCompression} disabled={!file}>Compress</button>
            {file && (
                <video 
                    ref={videoRef} 
                    src={URL.createObjectURL(file)} 
                    controls muted playsInline 
                    style={{ maxWidth: '400px', margin: '10px 0' }} 
                />
            )}
            {compressedFile && (
                <div>
                    <p>压缩完成:</p>
                    <video 
                        src={compressedFile} 
                        controls muted playsInline 
                        style={{ maxWidth: '400px', margin: '10px 0' }} 
                    />
                    <a href={compressedFile} download="compressed-video.mp4">下载压缩视频</a>
                </div>
            )}
        </>
    )
}

export default Test2

关键修复说明

  1. 等待视频加载与播放:将视频加载过程封装为Promise,确保元数据加载完成后再捕获流;添加muted和playsInline属性,绕过浏览器自动播放限制,保证视频能正常播放从而捕获流。
  2. 直接使用流对象:不再依赖useState存储mediaStream,而是在视频加载完成后直接使用捕获到的流,避免异步更新导致的延迟问题。
  3. 正确绑定MediaRecorder事件:将ondataavailable改为普通回调函数,仅收集有效数据块,避免空数据干扰。
  4. 控制录制生命周期:监听视频onended事件,播放结束后停止录制并关闭流轨道,确保生成完整的压缩视频;同时添加URL对象清理,避免内存泄漏。
  5. 配置压缩参数:在MediaRecorder构造函数中指定视频比特率,通过调整videoBitsPerSecond值控制压缩程度,数值越小压缩率越高、质量越低。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 03:10:13