Forms and files¶
FastAPI reads form fields and uploaded files with Form(), File() and UploadFile, from multipart or
URL-encoded bodies. The binary parses the same bodies and validates them with FastAPI's rules and errors.
Form fields with a constraint, one uploaded file with a form field next to it, a list of files, and
a file read as bytes. Like every example on this site, it is compiled and compared with FastAPI in CI (how).
from typing import Annotated
from fastapi import APIRouter, File, Form, UploadFile
router = APIRouter(prefix="/files", tags=["files"])
@router.post("/login")
async def login(username: Annotated[str, Form()], password: Annotated[str, Form(min_length=8)]):
return {"username": username}
@router.post("/upload")
async def upload(file: UploadFile, note: Annotated[str | None, Form()] = None):
content = await file.read()
return {
"filename": file.filename,
"content_type": file.content_type,
"size": len(content),
"lines": content.decode().count("\n"),
"note": note,
}
@router.post("/uploads")
async def upload_many(files: list[UploadFile]):
return [{"filename": f.filename, "size": f.size} for f in files]
@router.post("/raw")
async def raw_bytes(data: Annotated[bytes, File()]):
return {"size": len(data)}
Left to Python in this example
POST /files/raw declares data: Annotated[bytes, File()]: py2axum only translates File() parameters
typed UploadFile or list[UploadFile], so py2axum check reports it and --python-side auto relays it.
Use UploadFile and await file.read() to keep it native.
What is native¶
Form()/File()/UploadFile(single, optional or list, a literal default such asFile(default=[]), a fresh copy per request; multipart viamulteror urlencoded; an empty string counts as absent like FastAPI;UploadFilein memory:read,seek,filename,content_type,size,.file).OAuth2PasswordRequestFormis a form too (Security).- A
File()parameter typedbytes,bytes | Noneorlist[bytes]gets the uploaded file's content, as FastAPI's_extract_form_body(a text field is validated as is, Pydantic encoding astr; a text field in alist[bytes]is FastAPI's 500,'str' object has no attribute 'read').bytesfollows Pydantic: astris encoded to UTF-8 (lax),min_length/max_lengthcount bytes, JSON output is the UTF-8 text (invalid UTF-8:PydanticSerializationError, a 500). - A model of form fields (
Annotated[Model, Form()]): its fields read from the form, every other field added, then validated as the model at["body"](FastAPI's_extract_form_body); refused next to another form or body parameter.
Sending a file back is FileResponse (Responses); reading files on the server uses pathlib
and open() (Standard library). Multipart limits and the handling of uploaded file
names are in Security § Request input and
Security § Files and paths.
What stays in Python¶
- A
File()parameter of another type thanUploadFile,bytesor a list of them is refused.