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

FastAPI WebSocket处理Media Recorder Blobs:后续视频片段损坏问题

问题分析与解决方案

核心问题原因

1. 文件句柄管理不当

你的代码中每次循环都会打开目标文件,但仅当缓冲达到阈值时才关闭文件。这会导致:

  • 未关闭的文件无法将缓冲数据刷入磁盘,造成文件内容不完整
  • 文件句柄泄漏,后续写入操作可能出现不可预料的错误
  • 切换到新文件时,旧文件的句柄未释放,直接导致文件结构损坏

2. 缓冲逻辑存在漏洞

当bufferedSize达到阈值后,你直接写入当前数据并关闭文件,但未考虑当前数据的长度——如果单段Blob本身超过5MB,会导致单个文件大小超标;同时,关闭文件后未处理剩余数据的衔接,后续文件的起始数据不完整。

3. 缺少异常安全的文件操作

未使用Python的with语句管理文件上下文,一旦循环中出现异常,文件会处于未关闭状态,直接引发文件损坏。

修正后的后端代码

import os
from datetime import datetime
from fastapi import WebSocket, Query, Depends
# 替换为你项目中对应的导入路径
from your_auth_module import AuthJWT
from your_websocket_manager import manager

@app.websocket("/video")
async def websocket_endpoint(websocket: WebSocket, token: str = Query(...), videoId: str = Query(...), authorize: AuthJWT = Depends()):
    await manager.connect(websocket)

    temp_dir = os.path.join("temp", videoId)
    os.makedirs(temp_dir, exist_ok=True)

    queue_number = 1
    buffered_size = 0
    total_required = 5 * 1024 * 1024  # 5MB阈值
    current_file_path = os.path.join(temp_dir, f"{videoId}-video-{queue_number}.webm")
    
    print(f"{datetime.utcnow()}: Client connected: {websocket.client.host}")
    try:
        while True:
            data = await websocket.receive_bytes()
            data_len = len(data)
            
            # 计算当前文件剩余可写入的空间
            remaining_space = total_required - buffered_size
            
            if data_len <= remaining_space:
                # 当前数据可全部写入当前文件
                with open(current_file_path, 'ab') as f:
                    f.write(data)
                buffered_size += data_len
                print(f"缓冲大小: {buffered_size / (1024*1024):.2f}MB | 当前Blob大小: {data_len/(1024*1024):.2f}MB")
            else:
                # 拆分数据:先写满当前文件,剩余部分写入新文件
                if remaining_space > 0:
                    with open(current_file_path, 'ab') as f:
                        f.write(data[:remaining_space])
                    print(f"完成文件 {queue_number} | 最终大小: {total_required/(1024*1024):.2f}MB")
                
                # 处理剩余数据到新文件
                remaining_data = data[remaining_space:]
                queue_number += 1
                current_file_path = os.path.join(temp_dir, f"{videoId}-video-{queue_number}.webm")
                with open(current_file_path, 'ab') as f:
                    f.write(remaining_data)
                buffered_size = len(remaining_data)
                print(f"开始新文件 {queue_number} | 缓冲大小: {buffered_size/(1024*1024):.2f}MB")
            
            # 刚好达到阈值时,切换到新文件
            if buffered_size >= total_required:
                queue_number += 1
                current_file_path = os.path.join(temp_dir, f"{videoId}-video-{queue_number}.webm")
                buffered_size = 0
                print(f"完成文件 {queue_number-1} | 准备接收新文件")

    except WebSocketDisconnect:
        # 断开连接时保存未完成的文件
        if buffered_size > 0:
            print(f"客户端断开连接,保存未完成的文件 {queue_number}")
        print(f"{datetime.utcnow()}: Client disconnected: {websocket.client.host}")
        manager.disconnect(websocket)

额外注意事项

  • 前端MediaRecorder配置:初始化MediaRecorder时指定统一的MIME类型(如video/webm;codecs=vp9),避免不同浏览器编码差异导致的文件兼容性问题。
  • 文件完整性校验:可以在后端检查每个生成的WebM文件头是否包含WebM标识(\x1a\x45\xdf\xa3),快速排查无效文件。
  • 异步优化:如果文件写入成为性能瓶颈,可使用aiofiles库进行异步文件操作,避免阻塞WebSocket事件循环。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 15:53:15