Skip to content
On this page

How to handle file uploads in FastAPI

First of all, Start with utility function to save file to disk.

py
import os
from secrets import token_hex
from pathlib import Path
from fastapi import UploadFile

async def save_file_to_disk(file: UploadFile, path: str | Path) -> Path:
    # convert received `path` to `Path` object
    _path = Path(path)

    # create directory if not exists
    os.makedirs(_path, exist_ok=True)

    # generate unique file name
    new_file_name = token_hex(16) + Path(file.filename).suffix

    # Create file path for new file
    file_path = _path / new_file_name

    # Save file in chunks asynchronously
    with open(file_path, "wb") as buffer:
        async for chunk in file:
            buffer.write(chunk)
    return file_path

Next, Let's create a dependency that validates the file type. This dependency will be accept a list of allowed file types and raise an exception if the file type is not in the list.

py
from fastapi import HTTPException, UploadFile, Depends

class FileValidator:
    def __init__(self, allowed_types: list[str]):
        self.allowed_types = allowed_types

    async def __call__(self, file: UploadFile = File(...)):
        if file.content_type not in self.allowed_types:
            raise HTTPException(status_code=400, detail="Invalid file type")
        return file

# Dependency instances for different file types
img_validator = FileValidator(["image/jpeg", "image/png"])
pdf_validator = FileValidator(["application/pdf"])

Finally, Let's use this validator in our path operation:

py
from fastapi import FastAPI, UploadFile, Depends
from .deps import img_validator
from .utils import save_file_to_disk

app = FastAPI()

@app.post("/upload/images/")
async def upload_images(files: list[UploadFile] = Depends(img_validator)):
    # Create a list of tasks to save files to disk asynchronously
    upload_tasks = [save_file_to_disk(f) for f in files]

    # Wait for all tasks to complete
    file_paths = await asyncio.gather(*upload_tasks)

    # Return file paths
    return {"file_paths": file_paths}

In addition to this you can also add file size validation, file name validation, etc as per your requirements using dependencies.

Last updated: