Handling Forms and File Uploads
When you need to receive data from HTML forms or handle file uploads instead of JSON, FastAPI provides the Form and File classes. These classes allow you to define parameters that are extracted from the request body as application/x-www-form-urlencoded or multipart/form-data.
To use forms and files, you must install the python-multipart package:
pip install python-multipart
Receiving Form Fields
To receive form fields, use the Form class from fastapi. This class inherits from Body and specifically sets the media_type to application/x-www-form-urlencoded.
from typing import Annotated
from fastapi import FastAPI, Form
app = FastAPI()
@app.post("/login/")
async def login(username: Annotated[str, Form()], password: Annotated[str, Form()]):
return {"username": username}
In this example, username and password are extracted from the form-encoded body. If you define these parameters without Form(), FastAPI will look for them in the query parameters or expect a JSON body.
Handling File Uploads
For file uploads, use the File class. You can receive files as bytes or as an UploadFile object.
Uploading as Bytes
When you define a parameter as bytes, FastAPI reads the entire file into memory. This is suitable for small files.
from typing import Annotated
from fastapi import FastAPI, File
app = FastAPI()
@app.post("/files/")
async def create_file(file: Annotated[bytes, File()]):
return {"file_size": len(file)}
Uploading with UploadFile
For larger files, use UploadFile. It uses a "spooled" file, meaning it stays in memory up to a certain limit and then gets stored on disk, preventing your application from consuming too much RAM.
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, "content_type": file.content_type}
UploadFile provides several attributes and methods:
filename: The original filename (e.g.,myimage.jpg).content_type: The strategy type (e.g.,image/jpeg).file: A standard Python file-like object.read(),write(),seek(),close(): Async methods to interact with the file.
Mixing Forms and Files
You can define both Form and File parameters in the same path operation. When you do this, FastAPI ensures the request is handled as multipart/form-data.
from typing import Annotated
from fastapi import FastAPI, File, Form, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(
file: Annotated[bytes, File()],
fileb: Annotated[UploadFile, File()],
token: Annotated[str, Form()],
):
return {
"file_size": len(file),
"token": token,
"fileb_content_type": fileb.content_type,
}
Using Pydantic Models for Forms
If you have many form fields, you can group them into a Pydantic model. You still use Annotated with Form() to tell FastAPI to extract the data from the form body.
from typing import Annotated
from fastapi import FastAPI, Form
from pydantic import BaseModel
app = FastAPI()
class UserForm(BaseModel):
username: str
lastname: str
age: int | None = None
@app.post("/user/")
def post_form(user: Annotated[UserForm, Form()]):
return user
Troubleshooting
Missing python-multipart
If you define a Form or File parameter but haven't installed python-multipart, FastAPI will raise a RuntimeError when the application starts:
RuntimeError: The `python-multipart` library must be installed to use form parsing.
Incorrect Package Installation
A common mistake is installing the multipart package instead of python-multipart. FastAPI specifically checks for the presence of python-multipart. If you encounter errors despite having a "multipart" package installed, ensure you ran:
pip uninstall multipart
pip install python-multipart
Memory Usage
Be cautious when using bytes with File(). Since the entire file is loaded into memory, uploading very large files can cause your server to run out of RAM. Always prefer UploadFile for handling files of unknown or large sizes.