FastAPI单接口同时返回带音频的MP4视频与JSON/文本数据的实现方案咨询
FastAPI单接口同时返回带音频的MP4视频与JSON/文本数据的实现方案咨询
兄弟,我明白你想在一个接口里同时返回带音频的MP4和文本/JSON数据的需求,但这里有个核心问题——HTTP响应只能有一种媒体类型,没法同时把二进制视频流和JSON格式的文本塞进同一个响应里,所以你原来的代码直接返回字典肯定会报错。给你两个可行的方案,看哪种更适合你的场景:
方案一:拆分接口(推荐,简单易维护)
这是最符合HTTP规范的做法:一个接口负责返回带音频的视频流,另一个接口返回文本/JSON数据,前端页面同时请求这两个接口即可。
1. 修改后端代码
保留原有的视频流接口,新增一个返回文本数据的接口:
from http import HTTPStatus from pathlib import Path import uvicorn from fastapi import FastAPI, Header, Request, Response from fastapi.templating import Jinja2Templates app = FastAPI() templates = Jinja2Templates(directory="templates") CHUNK_SIZE = 1024*1024 video_path = Path("dataclasses_part2.mp4") @app.get("/") async def read_root(request: Request): return templates.TemplateResponse("index.htm", context={"request": request}) # 视频流接口(修复原有逻辑,确保带音频的视频正常播放) @app.get("/video") async def video_endpoint(range: str = Header(None)): start, end = range.replace("bytes=", "").split("-") start = int(start) end = int(end) if end else start + CHUNK_SIZE # 防止end超出文件大小 end = min(end, video_path.stat().st_size - 1) with open(video_path, "rb") as video: video.seek(start) video_data = video.read(end - start + 1) # 修正字节读取范围 filesize = str(video_path.stat().st_size) headers = { 'Content-Range': f'bytes {start}-{end}/{filesize}', 'Accept-Ranges': 'bytes', 'Content-Length': str(end - start + 1), } return Response( video_data, status_code=HTTPStatus.PARTIAL_CONTENT, headers=headers, media_type="video/mp4" ) # 新增:返回文本/JSON数据的接口 @app.get("/video-info") async def get_video_info(): data_oper = "Some info" # 这里可以替换成你的业务逻辑生成的文本 return {"text_data": data_oper} if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000)
2. 修改前端HTML模板
用JavaScript请求新增的接口,把文本数据渲染到页面上:
<!DOCTYPE html> <html> <head> <title>FastAPI video streaming</title> </head> <body> <video width="1200" controls muted="muted"> <source src="http://localhost:8000/video" type="video/mp4"/> </video> <h1 id="video-info">Loading info...</h1> <script> // 异步请求视频文本信息 fetch('/video-info') .then(response => response.json()) .then(data => { document.getElementById('video-info').textContent = data.text_data; }) .catch(error => { console.error('加载视频信息失败:', error); document.getElementById('video-info').textContent = '加载信息失败'; }); </script> </body> </html>
方案二:单接口返回Multipart混合数据(不推荐,前端处理复杂)
如果一定要用单个接口返回两种数据,只能用multipart/form-data格式打包视频流和JSON,但浏览器的<video>标签无法直接解析这种格式,需要前端手动拆分数据并转换,复杂度很高。
后端示例代码
from http import HTTPStatus from pathlib import Path import json import uvicorn from fastapi import FastAPI, Header, Response from multipart.multipart import MultipartEncoder app = FastAPI() CHUNK_SIZE = 1024*1024 video_path = Path("dataclasses_part2.mp4") @app.get("/combined") async def combined_endpoint(range: str = Header(None)): # 1. 获取视频流数据(和方案一的视频接口逻辑一致) start, end = range.replace("bytes=", "").split("-") start = int(start) end = int(end) if end else start + CHUNK_SIZE end = min(end, video_path.stat().st_size - 1) with open(video_path, "rb") as video: video.seek(start) video_data = video.read(end - start + 1) # 2. 准备文本数据 text_data = {"text_data": "Some info"} # 3. 打包成Multipart格式 encoder = MultipartEncoder( fields={ "video": ("video.mp4", video_data, "video/mp4"), "info": ("info.json", json.dumps(text_data), "application/json") } ) return Response( encoder, media_type=encoder.content_type, status_code=HTTPStatus.PARTIAL_CONTENT )
前端处理说明
前端需要手动解析Multipart响应,提取视频流并转换成Blob,再生成URL给<video>标签,这部分代码繁琐且容易出问题,除非有特殊业务需求,否则不建议使用。
备注:内容来源于stack exchange,提问作者Motixa
相关产品推荐
相关产品推荐

