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

如何在FastAPI中实现本地视频到浏览器的流式传输

FastAPI视频流加载异常修复方案

你遗漏的核心配置

  • 范围参数读取逻辑错误:浏览器发起范围请求时,字节区间是放在Range请求头中(格式为Range: bytes=<起始字节>-<结束字节>),不是拼接在URL查询参数里的bytes={start}-{end},你之前手动传查询参数能通,不代表能处理浏览器默认的请求逻辑。
  • 缺失必要的响应头:返回206部分内容响应时,必须携带三个核心头,缺任意一个都会导致浏览器中断后续请求:
    • Accept-Ranges: bytes:显式告知浏览器当前服务支持字节范围请求,不加这个头的话绝大多数浏览器发1-2次请求拿不到正确标识就会停止拉流
    • Content-Range:固定格式为bytes <当前返回段起始位置>-<当前返回段结束位置>/<视频总字节长度>
    • Content-Length:这里要填当前返回的字节段长度,不是整个视频的总长度,数值计算错误会被浏览器判定为响应异常
  • Media type配置错误:multipart/x-mixed-replace是MJPEG实时监控流专用的类型,普通MP4渐进式加载/点播场景固定用video/mp4即可,错用类型会导致浏览器无法正确解析流内容。
  • 响应内容截取逻辑错误:使用StreamingResponse时,需要根据解析到的Range值对字节流做seek定位,只返回对应区间的字节片段,不要每次都从头返回全量数据。

video标签的范围请求行为说明

  • 标准HTML的<video>标签默认就会自动携带Range请求头发起范围请求,不需要加任何额外属性,只要服务端响应符合规范,会自动分段拉取视频、支持进度条拖动。
  • 只有两种场景下video标签不会正常发起范围请求:
    • 服务端没有返回Accept-Ranges: bytes头,浏览器判定服务端不支持范围请求
    • 跨域场景下CORS配置没有把Content-Range、Accept-Ranges、Content-Length加入暴露头列表,浏览器无法读取这些核心响应头就会中断请求

最小可用实现参考

from fastapi import FastAPI, Request
from fastapi.responses import StreamingResponse

app = FastAPI()
# 提前加载视频数据到内存、计算总字节长度,实际场景可替换为动态读取逻辑
with open("your_video.mp4", "rb") as f:
    video_bytes = f.read()
video_total_size = len(video_bytes)

@app.get("/video")
async def serve_video(request: Request):
    range_header = request.headers.get("range")
    # 普通MP4固定用这个media type
    media_type = "video/mp4"

    # 未携带Range请求头时返回全量内容
    if not range_header:
        return StreamingResponse(
            iter([video_bytes]),
            media_type=media_type,
            headers={
                "Accept-Ranges": "bytes",
                "Content-Length": str(video_total_size)
            }
        )

    # 解析Range头内容
    start_str, end_str = range_header.replace("bytes=", "").split("-")
    start = int(start_str)
    # 未指定结束位置时取到视频末尾
    end = int(end_str) if end_str else video_total_size - 1
    chunk_size = end - start + 1
    # 截取对应区间的字节片段
    video_chunk = video_bytes[start:end+1]

    # 范围请求固定返回206状态码
    return StreamingResponse(
        iter([video_chunk]),
        status_code=206,
        media_type=media_type,
        headers={
            "Accept-Ranges": "bytes",
            "Content-Range": f"bytes {start}-{end}/{video_total_size}",
            "Content-Length": str(chunk_size)
        }
    )

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 01:12:26