File Upload + Save + List
Upload files via multipart form, save to disk with a UUID name, list all uploaded files. The pattern behind OpenBenchML's model upload.
🚀 Mini Project · 2 filesProject files
from fastapi import FastAPI, UploadFile, File, HTTPException
from fastapi.responses import HTMLResponse, FileResponse
from pathlib import Path
import uuid, uvicorn
app = FastAPI()
UPLOAD_DIR = Path('uploads')
UPLOAD_DIR.mkdir(exist_ok=True)
@app.get('/')
async def form():
return HTMLResponse('''
<form method='post' action='/upload' enctype='multipart/form-data'>
<input type='file' name='file' required>
<button>Upload</button>
</form>
<a href='/files'>View uploaded files</a>
''')
@app.post('/upload')
async def upload(file: UploadFile = File(...)):
# Validate extension
if not file.filename.endswith(('.pkl', '.csv', '.png', '.txt')):
raise HTTPException(400, 'unsupported file type')
# Generate a unique name to avoid collisions
ext = Path(file.filename).suffix
safe_name = f'{uuid.uuid4().hex}{ext}'
dest = UPLOAD_DIR / safe_name
# Stream to disk (don't load into memory — large files)
with open(dest, 'wb') as f:
while chunk := await file.read(1024 * 1024): # 1MB chunks
f.write(chunk)
return {'saved_as': safe_name, 'size_bytes': dest.stat().st_size}
@app.get('/files')
async def list_files():
files = [
{'name': f.name, 'size': f.stat().st_size}
for f in UPLOAD_DIR.iterdir() if f.is_file()
]
return files
@app.get('/download/{name}')
async def download(name: str):
f = UPLOAD_DIR / name
if not f.exists():
raise HTTPException(404)
return FileResponse(f)
if __name__ == '__main__':
uvicorn.run(app, host='0.0.0.0', port=8000)
pip install fastapi uvicorn python-multipart python main.py # Open http://localhost:8000 in a browser # Pick a .pkl, .csv, .png, or .txt file → Upload # Click 'View uploaded files' to see the saved list # Each file gets a UUID name to prevent collisions
How it works
File upload has 3 critical details: (1) enctype='multipart/form-data' on the form — without it, the file isn't sent. (2) UploadFile = File(...) on the server side — FastAPI streams the file, it doesn't load it all into RAM. (3) Stream to disk in chunks — `while chunk := await file.read(1MB): f.write(chunk)`. Reading the whole file into memory would crash the server on a 2GB upload. Other good practices: validate the extension (defends against malicious uploads), generate a UUID filename (prevents collisions and path traversal attacks — never trust the user's filename), and store size for display. The download endpoint uses FileResponse which streams the file back to the browser.
⚡ Open in Notebook