I am using FastAPI to return a video response from googlevideo.com
. This is the code I am using:
@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")
but this is not working
I want this Nodejs
to be converted into Python FastAPI:
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(); }); }); });
The quick solution would be to replace
yield from io.BytesIO(resp.read())
with the one below (see FastAPI documentation -StreamingResponse
for more details).However, instead of using
urllib.request
andresp.read()
(which would read the entire file contents into memory, hence the reason for taking too long to respond), I would suggest using theHTTPX
library, which, in contrast tourllib
andrequests
libraries, providesasync
support as well, which is more suitable in anasync
environment such as FastAPI's. Also, it supports Streaming Responses (seeasync
Streaming Responses too), and thus, you can avoid loading the entire response body into memory at once (especially, when dealing with large files). Below are provided examples in both synchronous and asynchronous ways on how to stream a video from a given URL.Note: Both versions below would allow multiple clients to connect to the server and get the video stream without being blocked, as a normal
def
endpoint in FastAPI is run in an external threadpool that is then awaited, instead of being called directly (as it would block the server)—thus ensuring that FastAPI will still work asynchronously. Even if you defined the endpoint of the first example below withasync def
instead, it would still not block the server, asStreamingResponse
will run the code (for sending the body chunks) in an external threadpool that is then awaited (have a look at this comment and the source code here), if the function for streaming the response body (i.e.,iterfile()
in the examples below) is a normal generator/iterator (as in the first example) and not anasync
one (as in the second example). However, if you had some other I/O or CPU blocking operations inside that endpoint, it would result in blocking the server, and hence, you should drop theasync
definition on that endpooint. The second example demonstrates how to implement the video streaming in anasync def
endpoint, which is useful when you have to call otherasync
functions inside the endpoint that you have toawait
, as well as you thus save FastAPI from running the endpoint in an external threadpool. For more details ondef
vsasync def
, please have a look at this answer.The below examples use
iter_bytes()
andaiter_bytes()
methods, respectively, to get the response body in chunks. These functions, as described in the documentation links above and in the source code here, can handle gzip, deflate, and brotli encoded responses. One can alternatively use theiter_raw()
method to get the raw response bytes, without applying content decoding (if is not needed). This method, in contrast toiter_bytes()
, allows you to optionally define thechunk_size
for streaming the response content, e.g.,iter_raw(1024 * 1024)
. However, this doesn't mean that you read the body in chunks of that size from the server (that is serving the file) directly. If you had a closer look at the source code ofiter_raw()
, you would see that it just uses aByteChunker
that stores the byte contents into memory (usingBytesIO()
stream) and returns the content in fixed-size chunks, depending the chunk size you passed to the function (whereasraw_stream_bytes
, as shown in the linked source code above, contains the actual byte chunk read from the stream).Using
HTTPX
withdef
endpointUsing
HTTPX
withasync def
endpointYou can use public videos provided here to test the above. Example:
If you would like to return a custom
Response
orFileResponse
instead—which I wouldn't really recommend in case you are dealing with large video files, as you should either read the entire contents into memory, or save the contents to a temporary file on disk that you later have to read again into memory, in order to send it back to the client—please have a look at this answer and this answer.