Multipart-uploads og indholdsvalidering
Modtag UploadFile-input, validér MIME-typer og størrelsesgrænser, og beskyt mod skadelige payloads.
Multipart-uploads og indholdsvalidering er en gratis Bootcamp i FastAPI-backendudvikling-lektion på CoddyKit. Dette er lektion 1 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Bootcamp i FastAPI-backendudvikling, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Bootcamp i FastAPI-backendudvikling-kurset indeholder 4 lektioner i alt.
Hvorfor multipart-upload er vigtigt
Almindelige JSON-requestbodyer kan ikke effektivt indeholde rå binære filer. Når en browser uploader et billede, en PDF eller en video, sender den en multipart/form-data-request, som pakker hvert felt (tekstværdier og filbytes) i separate dele med deres egne headere.
FastAPI giver adgang til indkommende filer gennem to hjælpere:
- UploadFile — et spool-baseret filobjekt, der automatisk opbevarer små filer i hukommelsen og store filer på disken.
- File() — en parameter-markør, der fortæller FastAPI, at værdien skal læses fra multipart-bodyen.
I denne lektion lærer du at modtage uploads, validere deres MIME-type og størrelse samt afvise skadelige eller for store payloads, før de berører dit lager.
Dit første UploadFile-endpoint
En UploadFile-parameter giver dig det oprindelige filename, den angivne content_type og asynkrone metoder som read() og seek(). Erklær den altid med = File(...), så FastAPI parser den fra multipart-bodyen.
Bemærk, at handleren er async, fordi fil-I/O på UploadFile kan afventes.
from fastapi import FastAPI, UploadFile, File
app = FastAPI()
@app.post("/upload")
async def upload(file: UploadFile = File(...)):
contents = await file.read()
return {
"filename": file.filename,
"content_type": file.content_type,
"size_bytes": len(contents),
}Stol aldrig på den angivne content_type
content_type på en UploadFile kommer direkte fra klienten. En angriber kan mærke en .exe-fil som image/png. Brug værdien som et billigt første filter, men aldrig som din eneste kontrol.
En robust behandlingskæde gør tre ting i denne rækkefølge:
- Afvis hurtigt åbenlyst forkerte angivne typer (billigt).
- Håndhæv en hård størrelsesgrænse under streaming (forhindrer udtømning af hukommelsen).
- Undersøg de faktiske filbytes (magiske tal) for at bekræfte den rigtige type.
De næste scener bygger hvert lag.
Tilladte MIME-typer
Brug altid en tilladelsesliste, aldrig en blokeringsliste. Angiv præcis de typer, du understøtter, og afvis alt andet. Returnér 415 Unsupported Media Type, når den angivne type ikke er tilladt.
Hold sættet lille og eksplicit, så nye filformater bliver et bevidst valg og ikke en tilfældighed.
from fastapi import FastAPI, UploadFile, File, HTTPException
app = FastAPI()
ALLOWED_TYPES = {"image/jpeg", "image/png", "application/pdf"}
@app.post("/documents")
async def create_document(file: UploadFile = File(...)):
if file.content_type not in ALLOWED_TYPES:
raise HTTPException(
status_code=415,
detail=f"Unsupported type: {file.content_type}",
)
return {"ok": True, "filename": file.filename}Håndhævelse af en størrelsesgrænse under streaming
Hvis du kalder await file.read(), indlæses hele filen i hukommelsen. En upload på 2 GB kan få din worker til at gå ned. Læs i stedet faste blokke, og afbryd i det øjeblik den løbende total overskrider din grænse.
Returnér 413 Request Entity Too Large, når grænsen overskrides. Det holder hukommelsesforbruget afgrænset, uanset hvor stor klienten hævder, at filen er.
from fastapi import FastAPI, UploadFile, File, HTTPException
app = FastAPI()
MAX_SIZE = 5 * 1024 * 1024 # 5 MB
CHUNK = 1024 * 1024 # 1 MB
@app.post("/upload")
async def upload(file: UploadFile = File(...)):
total = 0
while chunk := await file.read(CHUNK):
total += len(chunk)
if total > MAX_SIZE:
raise HTTPException(413, "File too large")
return {"filename": file.filename, "size": total}Sikker streaming direkte til disken
Når størrelse og type er godkendt, skal du streame blokkene til en destinationsfil i stedet for at holde dem i hukommelsen. Kombinér størrelseskontrollen med skrive-løkken, så du stopper tidligt ved for store payloads og aldrig gemmer en delvis, men enorm fil.
Brug await file.seek(0), hvis du tidligere har læst streamen og skal begynde forfra.
import aiofiles
from fastapi import UploadFile, File, HTTPException
MAX_SIZE = 5 * 1024 * 1024
async def save_upload(file: UploadFile, dest: str) -> int:
total = 0
async with aiofiles.open(dest, "wb") as out:
while chunk := await file.read(1024 * 1024):
total += len(chunk)
if total > MAX_SIZE:
raise HTTPException(413, "File too large")
await out.write(chunk)
return totalKontrol af det faktiske indhold med magiske tal
Den mest pålidelige typekontrol undersøger filens første bytes, dens magiske tal. En PNG-fil begynder altid med \x89PNG\r\n\x1a\n, en JPEG-fil med \xff\xd8\xff og en PDF-fil med %PDF.
Denne rene Python-funktion mapper et bytepræfiks til en faktisk MIME-type. Du kan køre den på en online-dommer helt uden framework.
def sniff_mime(head: bytes) -> str | None:
signatures = {
b"\x89PNG\r\n\x1a\n": "image/png",
b"\xff\xd8\xff": "image/jpeg",
b"%PDF": "application/pdf",
}
for magic, mime in signatures.items():
if head.startswith(magic):
return mime
return None
if __name__ == "__main__":
print(sniff_mime(b"\x89PNG\r\n\x1a\nrest")) # image/png
print(sniff_mime(b"%PDF-1.7")) # application/pdf
print(sniff_mime(b"MZ\x90\x00")) # None (rejected)Krydskontrol af angivet og faktisk type
Kombinér lagene: Læs netop nok bytes til at identificere det magiske tal, bekræft, at det findes på din tilladelsesliste, og kontrollér, at det stemmer overens med det, klienten angav. Et misforhold (angivet image/png, men de faktiske bytes viser en PDF) er et stærkt tegn på en ondsindet eller fejlbehæftet klient, så afvis det.
Kald await file.seek(0) efter identifikationen, så hele filen stadig kan gemmes.
from fastapi import UploadFile, File, HTTPException
ALLOWED = {"image/png", "image/jpeg", "application/pdf"}
async def validate_type(file: UploadFile) -> str:
head = await file.read(8)
await file.seek(0)
real = sniff_mime(head)
if real not in ALLOWED:
raise HTTPException(415, "Content not allowed")
if file.content_type != real:
raise HTTPException(415, "Declared type mismatch")
return realRensning af filnavne
Brug aldrig det filnavn, klienten har sendt, direkte som lagersti. Navne som ../../etc/passwd muliggør sti-traversering, og usædvanlige tegn kan ødelægge filsystemer. Fjern mappedelen, behold et sikkert tegnsæt, og foretræk et genereret navn sammen med en valideret filendelse.
Denne hjælper er ren Python og kan køres af en online-dommer.
import re
import uuid
from pathlib import PurePosixPath
EXT_FOR = {"image/png": ".png", "image/jpeg": ".jpg", "application/pdf": ".pdf"}
def safe_name(original: str, mime: str) -> str:
base = PurePosixPath(original).name # drop any path parts
base = re.sub(r"[^A-Za-z0-9._-]", "_", base) # keep safe chars
ext = EXT_FOR.get(mime, "")
return f"{uuid.uuid4().hex}{ext}"
if __name__ == "__main__":
print(safe_name("../../etc/passwd", "image/png").endswith(".png"))
print("/" not in safe_name("weird name!.jpg", "image/jpeg"))Håndtering af flere filer på én gang
Hvis du vil modtage flere filer i én request, skal du erklære parameteren som en list[UploadFile]. Klienten sender det samme formularfeltnavn gentagne gange. Validér hver fil uafhængigt, og afvis hele requesten, hvis én af dem er ugyldig, så delvise uploads aldrig efterlader en inkonsistent tilstand.
from fastapi import FastAPI, UploadFile, File, HTTPException
app = FastAPI()
ALLOWED = {"image/png", "image/jpeg"}
@app.post("/gallery")
async def gallery(files: list[UploadFile] = File(...)):
if len(files) > 10:
raise HTTPException(400, "Too many files (max 10)")
for f in files:
if f.content_type not in ALLOWED:
raise HTTPException(415, f"{f.filename}: bad type")
return {"received": [f.filename for f in files]}Pak validering som en dependency
Det er fejlbehæftet at gentage type- og størrelseskontroller i hvert endpoint. Pak dem ind i en genanvendelig FastAPI-dependency. Dependency'en kører hele behandlingskæden og returnerer en ren, valideret UploadFile, så din route kan fokusere på forretningslogikken.
Det er strukturen til produktion: tilladelsesliste, streamet størrelsesgrænse, kontrol af magisk tal og sikre filnavne samlet ét sted.
from fastapi import Depends, UploadFile, File, HTTPException
MAX_SIZE = 5 * 1024 * 1024
async def validated_upload(file: UploadFile = File(...)) -> UploadFile:
head = await file.read(8)
if sniff_mime(head) not in {"image/png", "image/jpeg", "application/pdf"}:
raise HTTPException(415, "Unsupported content")
total = len(head)
while chunk := await file.read(1024 * 1024):
total += len(chunk)
if total > MAX_SIZE:
raise HTTPException(413, "File too large")
await file.seek(0)
return file
@app.post("/secure-upload")
async def secure_upload(file: UploadFile = Depends(validated_upload)):
return {"filename": file.filename}Hurtig kontrol: Vælg den rigtige beskyttelse
Et endpoint modtager profilbilleder. En bruger uploader en fil på 3 GB, hvis content_type-header hævder image/png, men hvis bytes faktisk er en eksekverbar fil. Hvilken ene kombination af kontroller beskytter serveren pålideligt?
Opsummering: Et lagdelt forsvar mod uploads
Du har opbygget en komplet, defensiv upload-behandlingskæde til FastAPI:
- UploadFile + File() modtager multipart-data med I/O, der egner sig til streaming.
- Sæt den angivne MIME-type på tilladelseslisten, og returnér
415for alt uventet, men stol aldrig alene på headeren. - Stream i blokke, og afbryd med
413, så snart en størrelsesgrænse overskrides, så hukommelsesforbruget holdes afgrænset. - Undersøg magiske tal for at bekræfte den faktiske indholdstype, og afvis misforhold mellem angivet og faktisk type.
- Rens filnavne med genererede navne for at forhindre sti-traversering.
- Pak det som en dependency, så hvert endpoint genbruger den samme beskyttelse.
Lagdelt kontrol, ordnet fra billig til dyr, giver robust beskyttelse mod for store og skadelige uploads.
Lær Bootcamp i FastAPI-backendudvikling med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 21
- Lektioner
- 84
Ofte stillede spørgsmål
Er lektionen “Multipart-uploads og indholdsvalidering” gratis?
Ja — alle 3 lektioner i læringssporet Bootcamp i FastAPI-backendudvikling, inklusive “Multipart-uploads og indholdsvalidering”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Bootcamp i FastAPI-backendudvikling-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Multipart-uploads og indholdsvalidering”?
Modtag UploadFile-input, validér MIME-typer og størrelsesgrænser, og beskyt mod skadelige payloads. Du øver dig i Bootcamp i FastAPI-backendudvikling med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på Bootcamp i FastAPI-backendudvikling?
Der kræves ingen tidligere erfaring. Bootcamp i FastAPI-backendudvikling på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 1 af 4.
Hvor lang tid tager lektionen “Multipart-uploads og indholdsvalidering”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne Bootcamp i FastAPI-backendudvikling-lektion?
Ja. Alle Bootcamp i FastAPI-backendudvikling-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Multipart-uploads og indholdsvalidering
- Streaming-svar og range requests
- Aflastning af storage til S3-kompatible buckets
- Asynkron billed- og dokumenttransformation