How to Handle File Uploads in FastAPI
FastAPI is a modern, fast (high-performance), web framework for building APIs with Python based on standard Python type hints. One common requirement in many web applications is the ability to handle file uploads. Whether it’s a user uploading a profile picture, a document, or a media file, FastAPI provides a straightforward way to handle such scenarios. In this blog post, we’ll explore the fundamental concepts, usage methods, common practices, and best practices for handling file uploads in FastAPI.
Table of Contents
Fundamental Concepts
File and UploadFile
In FastAPI, there are two main ways to handle file uploads: using the File type and the UploadFile type.
File: It is used to receive file data as bytes. This is suitable for small files where you want to handle the data directly as a byte string.UploadFile: It is a more advanced way to handle file uploads. It represents an uploaded file and provides useful attributes and methods such asfilename,content_type, andfile(a file-like object that can be used to read the file’s contents). It is recommended for handling larger files as it reads the file in chunks, which is more memory-efficient.
Multipart Form Data
File uploads in web applications typically use the multipart/form-data encoding type. When a client sends a file to the server, the data is divided into multiple parts, each with its own headers and content. FastAPI can automatically parse this multipart/form-data and extract the file data for you.
Usage Methods
Using File
The following is a simple example of using the File type to handle a file upload:
from fastapi import FastAPI, File
app = FastAPI()
@app.post("/upload-file/")
async def upload_file(file: bytes = File()):
# Here we just return the length of the file data
return {"file_size": len(file)}
In this example, the file parameter is of type bytes, and FastAPI will automatically read the entire file contents into memory as a byte string.
Using UploadFile
The following is an example of using the UploadFile type:
from fastapi import FastAPI, UploadFile
app = FastAPI()
@app.post("/upload-file-uploadfile/")
async def upload_file_uploadfile(file: UploadFile = File()):
contents = await file.read()
# Here we just return the length of the file data
return {"filename": file.filename, "file_size": len(contents)}
In this example, the file parameter is of type UploadFile. We can access the file’s filename using file.filename and read the file’s contents using await file.read().
Common Practices
Handling Multiple File Uploads
FastAPI allows you to handle multiple file uploads by using a list of File or UploadFile types. Here is an example of handling multiple file uploads using UploadFile:
from fastapi import FastAPI, UploadFile
app = FastAPI()
@app.post("/upload-multiple-files/")
async def upload_multiple_files(files: list[UploadFile] = File()):
results = []
for file in files:
contents = await file.read()
results.append({"filename": file.filename, "file_size": len(contents)})
return results
Saving Uploaded Files
Often, you’ll want to save the uploaded files to disk. Here is an example of saving an uploaded file using UploadFile:
import os
from fastapi import FastAPI, UploadFile
app = FastAPI()
UPLOAD_DIR = "uploads"
if not os.path.exists(UPLOAD_DIR):
os.makedirs(UPLOAD_DIR)
@app.post("/save-file/")
async def save_file(file: UploadFile = File()):
file_path = os.path.join(UPLOAD_DIR, file.filename)
with open(file_path, "wb") as f:
contents = await file.read()
f.write(contents)
return {"message": f"File {file.filename} saved successfully"}
Best Practices
File Size Limitation
To prevent users from uploading extremely large files, you can add file size limitations. Here is an example of adding a file size limitation using a custom middleware:
from fastapi import FastAPI, File, UploadFile, Request, HTTPException
app = FastAPI()
MAX_FILE_SIZE = 1024 * 1024 # 1MB
@app.middleware("http")
async def check_file_size(request: Request, call_next):
content_length = request.headers.get("content-length")
if content_length and int(content_length) > MAX_FILE_SIZE:
raise HTTPException(status_code=413, detail="File size exceeds the limit")
response = await call_next(request)
return response
@app.post("/upload-limited-file/")
async def upload_limited_file(file: UploadFile = File()):
contents = await file.read()
return {"filename": file.filename, "file_size": len(contents)}
File Type Validation
You can also validate the file type to ensure that only certain types of files are allowed to be uploaded. Here is an example of validating the file type based on the file’s content type:
from fastapi import FastAPI, UploadFile, HTTPException
app = FastAPI()
ALLOWED_FILE_TYPES = ["image/jpeg", "image/png"]
@app.post("/upload-validated-file/")
async def upload_validated_file(file: UploadFile = File()):
if file.content_type not in ALLOWED_FILE_TYPES:
raise HTTPException(status_code=400, detail="Invalid file type")
contents = await file.read()
return {"filename": file.filename, "file_size": len(contents)}
Conclusion
Handling file uploads in FastAPI is relatively straightforward. By using the File and UploadFile types, you can easily receive and process file data. However, when dealing with file uploads, it’s important to consider factors such as file size limitation and file type validation to ensure the security and stability of your application. With the knowledge and examples provided in this blog post, you should be able to handle file uploads efficiently in your FastAPI applications.