Introduction
File uploads are a common feature in web applications, from profile pictures to reports and document processing. They also introduce risks that ordinary JSON endpoints do not face: unexpectedly large files, malicious content, exhausted storage, and unsafe filenames.
A reliable upload endpoint validates incoming data, limits resource consumption, stores files safely, and makes failures manageable.
1. Validate more than the filename
A filename such as report.pdf does not prove that the uploaded content is a PDF. Client-supplied content types can also be incorrect.
Validation should consider the allowed file types, actual file content where practical, maximum size, and business requirements. For high-risk use cases, malware scanning and content-disarm procedures may be necessary.
2. Set limits at the application boundary
FastAPI supports multipart uploads through UploadFile:
from fastapi import FastAPI, File, HTTPException, UploadFileapp = FastAPI()MAX_SIZE = 5 * 1024 * 1024ALLOWED_TYPES = {"image/jpeg", "image/png"}@app.post("/uploads")async def upload_file(file: UploadFile = File(...)): if file.content_type not in ALLOWED_TYPES: raise HTTPException( status_code=415, detail="Unsupported file type", ) content = await file.read(MAX_SIZE + 1) if len(content) > MAX_SIZE: raise HTTPException( status_code=413, detail="File is too large", ) return { "filename": file.filename, "size": len(content), }
This example reads a bounded amount of content and returns metadata; it does not save the file. Production systems should also enforce request-size limits at the reverse proxy or application server and consider streaming large uploads rather than buffering them entirely in memory.
3. Generate storage names
Never use a user-supplied filename directly as a filesystem path. Generate a random storage identifier and keep the original filename as metadata only if the application needs it.
Store uploads outside publicly executable directories. If users need to download files, serve them through an authorized endpoint or a carefully configured object-storage URL.
4. Keep file processing asynchronous when needed
Image resizing, document extraction, and malware scanning can be expensive. For larger workloads, store the uploaded object and enqueue a processing job instead of holding the HTTP request open.
Track processing states such as pending, processing, completed, and failed. Make retries safe and ensure users cannot access files before the required security checks finish.
5. Test failure cases
Test oversized uploads, unsupported formats, empty files, interrupted transfers, storage failures, and unauthorized access. Confirm that partial files are cleaned up and that temporary storage cannot grow without limit.
Conclusion
Secure file uploads require limits, content validation, safe storage, and controlled processing. Treat every uploaded file as untrusted input, and design the entire lifecycle—from upload to download—as part of the application’s security model.