提问者:小点点

在FastAPI中从在线视频URL返回文件/流响应


我正在使用FastAPI从googlevideo.com返回视频响应。这是我正在使用的代码:

@app.get(params.api_video_route)
async def get_api_video(url=None):

  def iter():
     req = urllib.request.Request(url)

     with urllib.request.urlopen(req) as resp:
         yield from io.BytesIO(resp.read())


  return StreamingResponse(iter(), media_type="video/mp4")

但这不管用

我希望这个Nodejs被转换成PythonFastAPI:

app.get("/download-video", function(req, res) { 
 http.get(decodeURIComponent(req.query.url), function(response) { 
   res.setHeader("Content-Length", response.headers["content-length"]); 
   if (response.statusCode >= 400)         
     res.status(500).send("Error");                     
     response.on("data", function(chunk) { res.write(chunk); }); 
     response.on("end", function() { res.end(); }); }); });

共2个答案

匿名用户

我遇到了类似的问题,但解决了所有问题。主要思想是使用request. Session()创建一个会话,并一个接一个地生成一个块,而不是获取所有内容并一次生成它。这非常好,根本不会产生任何内存问题。

@app.get(params.api_video_route)
async def get_api_video(url=None):

  def iter():
     session = requests.Session()
     r = session.get(url, stream=True)
     r.raise_for_status()
     
     for chunk in r.iter_content(1024*1024):
         yield chunk

  return StreamingResponse(iter(), media_type="video/mp4") 

匿名用户

快速解决方案是用下面的替换io. BytesIO(resp.read())的收益(有关更多详细信息,请参阅FastAPI留档-StreamingResponse)。

 yield from resp

然而,我建议使用HTTPX库,而不是使用urllib. request和resp.read()(这会将整个文件内容读取到内存中,因此需要太长时间才能响应),它与urllib和请求库相比,还提供了async支持,这更适合于FastAPI等async环境。此外,它还支持流式响应(也请参阅async流式响应),因此,您可以避免一次将整个响应体加载到内存中(尤其是在处理大文件时)。下面提供了同步和异步方式的示例,说明如何从给定URL流式传输视频。

from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import httpx

app = FastAPI()

@app.get('/video')
def get_video(url: str):

    def iterfile():
        with httpx.stream("GET", url) as r:
            for chunk in r.iter_bytes():
                yield chunk

    return StreamingResponse(iterfile(), media_type="video/mp4")
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import httpx

app = FastAPI()

@app.get('/video')
async def get_video(url: str):

    async def iterfile():
        async with httpx.AsyncClient() as client:
            async with client.stream("GET", url) as r:
                async for chunk in r.aiter_bytes():
                    yield chunk
                    
    return StreamingResponse(iterfile(), media_type="video/mp4")

您可以使用此处提供的公共视频来测试上述内容。示例:

http://127.0.0.1:8000/video?url=http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4

如果您想返回一个自定义的ResponseFileResponse-如果您正在处理大型视频文件,我真的不建议这样做,因为您应该将整个内容读取到内存中,或者将内容保存到磁盘上的临时文件中,您以后必须再次读取到内存中,以便将其发送回客户端-请查看此答案和此答案。