Skip to main content

File

Declare a file upload parameter for a path operation. File parameters receive uploaded files as bytes or UploadFile objects.
from typing import Annotated
from fastapi import FastAPI, File, UploadFile

app = FastAPI()

@app.post("/uploadfile/")
async def create_upload_file(
    file: Annotated[UploadFile, File()]
):
    return {"filename": file.filename}

Signature

def File(
    default: Any = Undefined,
    *,
    media_type: str = "multipart/form-data",
    alias: str | None = None,
    title: str | None = None,
    description: str | None = None,
    gt: float | None = None,
    ge: float | None = None,
    lt: float | None = None,
    le: float | None = None,
    min_length: int | None = None,
    max_length: int | None = None,
    pattern: str | None = None,
    examples: list[Any] | None = None,
    deprecated: bool | str | None = None,
    include_in_schema: bool = True,
    json_schema_extra: dict[str, Any] | None = None,
) -> Any

Parameters

default
Any
default:"Undefined"
Default value if the parameter field is not set.
media_type
str
default:"multipart/form-data"
The media type of this parameter field. For file uploads, this is always multipart/form-data.
alias
str | None
default:"None"
An alternative name for the parameter field. This will be used to extract the data and for the generated OpenAPI.
title
str | None
default:"None"
Human-readable title for the parameter.
description
str | None
default:"None"
Human-readable description for the parameter.
examples
list[Any] | None
default:"None"
Example values for this field.
deprecated
bool | str | None
default:"None"
Mark this parameter field as deprecated. It will affect the generated OpenAPI (visible at /docs).
include_in_schema
bool
default:"True"
Whether to include this parameter field in the generated OpenAPI.
json_schema_extra
dict[str, Any] | None
default:"None"
Any additional JSON schema data.

UploadFile Class

The UploadFile class provides an async-friendly interface for working with uploaded files.

Attributes

  • filename: str - The original filename
  • content_type: str - The content type (MIME type)
  • file: SpooledTemporaryFile - The actual Python file object
  • headers: Headers - The headers associated with the file
  • size: int - The size of the file in bytes

Methods

  • async read(size: int = -1): Read the file contents
  • async write(data: bytes): Write data to the file
  • async seek(offset: int): Move to a specific position in the file
  • async close(): Close the file

Examples

Upload Single File (UploadFile)

from typing import Annotated
from fastapi import FastAPI, File, UploadFile

app = FastAPI()

@app.post("/uploadfile/")
async def create_upload_file(
    file: Annotated[UploadFile, File()]
):
    contents = await file.read()
    return {
        "filename": file.filename,
        "content_type": file.content_type,
        "size": len(contents)
    }
UploadFile is recommended over bytes for file uploads because it uses a spooled file (stored in memory up to a size limit, then on disk), making it more memory-efficient for large files.

Upload Single File (bytes)

@app.post("/uploadfile/")
async def create_upload_file(
    file: Annotated[bytes, File()]
):
    return {"file_size": len(file)}
Using bytes loads the entire file into memory. This is only suitable for small files.

Upload Multiple Files

@app.post("/uploadfiles/")
async def create_upload_files(
    files: Annotated[list[UploadFile], File()]
):
    return {
        "filenames": [file.filename for file in files]
    }

Optional File Upload

@app.post("/uploadfile/")
async def create_upload_file(
    file: Annotated[UploadFile | None, File()] = None
):
    if file is None:
        return {"message": "No file uploaded"}
    return {"filename": file.filename}

File Upload with Form Data

from fastapi import Form

@app.post("/uploadfile/")
async def create_upload_file(
    file: Annotated[UploadFile, File()],
    description: Annotated[str, Form()],
    public: Annotated[bool, Form()] = False
):
    return {
        "filename": file.filename,
        "description": description,
        "public": public
    }

File Upload with Metadata

@app.post("/uploadfile/")
async def create_upload_file(
    file: Annotated[
        UploadFile,
        File(description="The file to upload")
    ]
):
    return {"filename": file.filename}

Working with UploadFile

Save File to Disk

@app.post("/uploadfile/")
async def create_upload_file(
    file: Annotated[UploadFile, File()]
):
    with open(f"uploads/{file.filename}", "wb") as buffer:
        content = await file.read()
        buffer.write(content)
    return {"filename": file.filename}

Read File in Chunks

@app.post("/uploadfile/")
async def create_upload_file(
    file: Annotated[UploadFile, File()]
):
    chunk_size = 1024 * 1024  # 1MB
    chunks = []
    while True:
        chunk = await file.read(chunk_size)
        if not chunk:
            break
        chunks.append(chunk)
    return {
        "filename": file.filename,
        "total_chunks": len(chunks)
    }

Process Image File

from PIL import Image
import io

@app.post("/uploadimage/")
async def upload_image(
    file: Annotated[UploadFile, File()]
):
    contents = await file.read()
    image = Image.open(io.BytesIO(contents))
    return {
        "filename": file.filename,
        "format": image.format,
        "size": image.size
    }

Validate File Type

@app.post("/uploadimage/")
async def upload_image(
    file: Annotated[UploadFile, File()]
):
    allowed_types = ["image/jpeg", "image/png", "image/gif"]
    if file.content_type not in allowed_types:
        raise ValueError(f"File type {file.content_type} not allowed")
    return {"filename": file.filename}

Common Use Cases

Profile Picture Upload

@app.post("/users/me/avatar")
async def upload_avatar(
    file: Annotated[UploadFile, File(description="Profile picture")]
):
    # Validate file is an image
    if not file.content_type.startswith("image/"):
        raise ValueError("File must be an image")
    
    # Save file
    with open(f"avatars/{file.filename}", "wb") as buffer:
        content = await file.read()
        buffer.write(content)
    
    return {"avatar_url": f"/avatars/{file.filename}"}

Document Upload

@app.post("/documents/")
async def upload_document(
    file: Annotated[UploadFile, File()],
    title: Annotated[str, Form()],
    tags: Annotated[str, Form()] = ""
):
    return {
        "filename": file.filename,
        "title": title,
        "tags": tags.split(",")
    }

Bulk File Upload

@app.post("/uploadfiles/")
async def upload_multiple_files(
    files: Annotated[list[UploadFile], File(description="Multiple files to upload")]
):
    results = []
    for file in files:
        contents = await file.read()
        results.append({
            "filename": file.filename,
            "size": len(contents)
        })
    return {"files": results}

Installation Requirement

File uploads require the python-multipart package:
pip install python-multipart

Important Notes

Always validate file types and sizes to prevent security issues and resource exhaustion.
UploadFile uses Python’s SpooledTemporaryFile, which stores the file in memory up to a maximum size, then stores it on disk. This makes it efficient for both small and large files.
Don’t forget to await file.read() since UploadFile methods are async.

Build docs developers (and LLMs) love