Beyond JSON: Form Data and File Uploads
Beyond JSON: Form Data and File Uploads
Section titled “Beyond JSON: Form Data and File Uploads”While JSON is the standard for modern APIs, sometimes you need to interact with classic HTML forms or handle file uploads. FastAPI handles these non-JSON requests gracefully, but they require a specific approach.
Setup: Installing python-multipart
Section titled “Setup: Installing python-multipart”To handle uploaded files and form data, FastAPI relies on an external library called python-multipart. Before proceeding, you must install it:
pip install python-multipartHandling Form Data
Section titled “Handling Form Data”When receiving data from an HTML <form>, the media type is usually application/x-www-form-urlencoded. This is different from JSON. To tell FastAPI to read from form data instead of JSON, use the Form class.
from typing import Annotatedfrom fastapi import FastAPI, Form
app = FastAPI()
@app.post("/login/")async def login(username: Annotated[str, Form()], password: Annotated[str, Form()]): return {"username": username}This works exactly like Body, Query, or Path. It tells FastAPI to look for username and password fields in the form data.
File Uploads: Bytes vs UploadFile
Section titled “File Uploads: Bytes vs UploadFile”FastAPI provides two ways to define file parameters: bytes (using File) and UploadFile.
1. File as bytes
Section titled “1. File as bytes”If you declare the type as bytes, FastAPI will read the entire file into memory.
from fastapi import File
@app.post("/files/")async def create_file(file: Annotated[bytes, File()]): return {"file_size": len(file)}This is simple for small files, but for large files (like videos), it can consume all your server’s memory and crash the application.
2. UploadFile (Recommended)
Section titled “2. UploadFile (Recommended)”Using UploadFile is safer and more feature-rich. It uses Python’s SpooledTemporaryFile. This means it stays in memory up to a certain size limit, and then automatically spills over to disk.
from fastapi import UploadFile
@app.post("/uploadfile/")async def create_upload_file(file: UploadFile): return {"filename": file.filename, "content_type": file.content_type}Comparison of the two approaches:
| Feature | bytes | UploadFile |
|---|---|---|
| Storage | Stored entirely in RAM | Spooled (RAM + Disk) |
| Performance | Fast for tiny files | Efficient for large files |
| Metadata | None (just raw data) | Has filename, content_type |
| Interface | Python bytes object | File-like object (read/write/close) |
Multiple File Uploads
Section titled “Multiple File Uploads”You can handle multiple files at once by declaring a list of UploadFile.
from typing import List
@app.post("/uploadfiles/")async def create_upload_files(files: List[UploadFile]): return {"filenames": [file.filename for file in files]}This endpoint will accept an HTML form sending multiple files (e.g., <input type="file" multiple>) and process them efficiently.