Upload files in FastAPI with file validation

File upload and validation can get tricky when it comes to FastAPI as we simply cannot use pydantic to do validations for us. Let's see how to get this done

from fastapi import UploadFile

@app.post("/file")
def upload_file(file: UploadFile):
    validate_file_size_type(file)

    return {
        "filename": file.filename,
        "content_type": file.content_type,
    }

Here I have a simple function which takes a file as a parameter. Since we have typed it as UploadFile. The request body has to be sent as multipart/form-data. Now let's check out our validate_file_size_type function.

from fastapi import HTTPException, status
from typing import IO


def validate_file_size_type(file: IO):
    FILE_SIZE = 2097152 # 2MB

    if file.content_type not in ["image/png", "image/jpeg", "image/jpg"]:
        raise HTTPException(
            status_code=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE,
            detail="Unsupported file type",
        )

    real_file_size = 0
    for chunk in file.file:
        real_file_size += len(chunk)
        if real_file_size > FILE_SIZE:
            raise HTTPException(status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE, detail="Too large")

In this function, we are validating if the file is of type image. Note that we are not just checking the extension of the file name as it can be changed. You can change the list for the expected file type like application/json for JSON files.

So, here's the thing, a file is not completely sent to the server and received by your FastAPI app before the code in the path operation starts to execute.

So, you don't have an actual way of knowing the actual size of the file before reading it.

You can check out this GitHub discussion for deeper understanding https://github.com/tiangolo/fastapi/issues/362