I would like to download a large file when the application starts up but it should happen parallelly. As in an actual app startup, the app shouldn't wait for the file download to complete.
What I am currently doing is:
from fastapi import FastAPI
app = FastAPI()
items = {}
@app.on_event("startup")
def startup_event():
//Download file
Now this seems to work but I get a lot of critical worker timeout errors. I wanted to know if there is someway that I can do the download just when the application starts but also do it in a way that it doesn't make the application wait for the download to finish.
This answer derives code and information from the following answers. Hence, please take a look at them for more details and explanation:
The solutions provided below use the httpx
library, which provides a powerful HTTP client library for Python, an async
API and support for both HTTP/1.1 and HTTP/2. The aiofiles
library is also used for handling file operations (such as writing files to disk) in asyncio
applications. Public videos (large files) for testing the solutions can be found here.
Use this solution if you would like to reuse the HTTP client across your application.
from fastapi import FastAPI, Request
from contextlib import asynccontextmanager
from fastapi.responses import StreamingResponse
from starlette.background import BackgroundTask
import asyncio
import aiofiles
import httpx
async def download_large_file(client: httpx.AsyncClient):
large_file_url = 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'
path = 'save_to/video.mp4'
req = client.build_request('GET', large_file_url)
r = await client.send(req, stream=True)
async with aiofiles.open(path, 'wb') as f:
async for chunk in r.aiter_raw():
await f.write(chunk)
await r.aclose()
@asynccontextmanager
async def lifespan(app: FastAPI):
# Initialize the Client on startup and add it to the state
async with httpx.AsyncClient() as client:
asyncio.create_task(download_large_file(client))
yield {'client': client}
# The Client closes on shutdown
app = FastAPI(lifespan=lifespan)
@app.get('/')
async def home():
return 'Hello World!'
@app.get('/download')
async def download_some_file(request: Request):
client = request.state.client # reuse the HTTP client
req = client.build_request('GET', 'https://www.example.com')
r = await client.send(req, stream=True)
return StreamingResponse(r.aiter_raw(), background=BackgroundTask(r.aclose))
Use this solution if you don't need reusing the HTTP client, but only need using it at startup.
from fastapi import FastAPI
from contextlib import asynccontextmanager
import asyncio
import aiofiles
import httpx
async def download_large_file():
large_file_url = 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'
path = 'save_to/video.mp4'
async with httpx.AsyncClient() as client:
async with client.stream('GET', large_file_url) as r:
async with aiofiles.open(path, 'wb') as f:
async for chunk in r.aiter_raw():
await f.write(chunk)
@asynccontextmanager
async def lifespan(app: FastAPI):
asyncio.create_task(download_large_file())
yield
app = FastAPI(lifespan=lifespan)
@app.get('/')
async def home():
return 'Hello World!'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With