Avlasting av lagring til S3-kompatible bøtter
Strøm opplastinger direkte til S3/MinIO med forhåndssignerte URL-er, slik at API-et forblir tilstandsløst og skalerbart.
Avlasting av lagring til S3-kompatible bøtter er en gratis leksjon i Bootcamp i backendutvikling med FastAPI på CoddyKit. Dette er leksjon 3 av 4. Du kan lese valgfritt 3 leksjoner fra denne læringsstien gratis i sin helhet – deretter låser CoddyKit PRO opp alle leksjoner, samt praktisk øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Bootcamp i backendutvikling med FastAPI, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Bootcamp i backendutvikling med FastAPI inneholder totalt 4 leksjoner.
Hvorfor flytte lagringen til S3?
Det skalerer ikke å lagre opplastede filer på API-serverens lokale disk. Hver replika trenger sin egen kopi, diskene blir fulle, og containere er flyktige — start podden på nytt, så forsvinner filene.
Løsningen er å flytte medier til et eksternt objektlager og holde API-et tilstandsløst. Alle S3-kompatible tjenester fungerer:
- Amazon S3 — originalen, fullt administrert.
- MinIO — selvvertet, kompatibelt med S3-API-et og godt egnet for utvikling og lokal drift.
- Cloudflare R2, Backblaze B2, DigitalOcean Spaces — billigere datautgang, samme API.
Fordi de alle bruker S3-protokollen, kan den samme boto3-klientkoden brukes mot alle ved å endre URL-en til endepunktet.
Konfigurere en boto3-S3-klient
Biblioteket boto3 er standard-SDK-en for AWS i Python. Hvis De vil bruke en annen leverandør enn AWS, for eksempel MinIO eller R2, sender De inn en eksplisitt endpoint_url.
Oppbevar legitimasjon og endepunkt i innstillinger, aldri hardkodet. Bruk config=Config(signature_version="s3v4"), slik at forhåndssignerte URL-er genereres med den moderne SigV4-algoritmen som alle leverandører godtar.
import boto3
from botocore.config import Config
def build_s3_client():
return boto3.client(
"s3",
endpoint_url="https://s3.eu-central-1.amazonaws.com",
aws_access_key_id="AKIA...",
aws_secret_access_key="secret...",
region_name="eu-central-1",
config=Config(signature_version="s3v4"),
)
client = build_s3_client()
print(type(client).__name__)Den naive metoden (og hvorfor den er problematisk)
Det åpenbare første forsøket er å lese hele opplastingen inn i minnet og deretter sende den til S3:
data = await file.read()laster hele filen inn i RAM.- En videoopplasting på 2 GB blir til 2 GB prosessminne — multipliser dette med samtidige forespørsler, så krasjer arbeiderne på grunn av tomt minne.
Dette fungerer for små avatarer, men er risikabelt for medier. Vi vil strømme byte gjennom API-et (eller hoppe over API-et helt ved å bruke forhåndssignerte URL-er). De neste delene bygger opp begge teknikkene.
from fastapi import FastAPI, UploadFile
app = FastAPI()
@app.post("/upload-naive")
async def upload_naive(file: UploadFile):
data = await file.read() # whole file in RAM - avoid for large media!
return {"size": len(data)}Strømme opplastinger med upload_fileobj
FastAPI sin UploadFile pakker inn en SpooledTemporaryFile: små opplastinger blir i minnet, mens store opplastinger automatisk flyttes til disken. Attributtet .file er et standard fillignende objekt.
upload_fileobj i boto3 leser denne strømmen i blokker og utfører en multipart-opplasting i bakgrunnen — dermed holder minnebruken seg begrenset uavhengig av filstørrelsen.
from fastapi import FastAPI, UploadFile
app = FastAPI()
BUCKET = "user-media"
@app.post("/upload")
async def upload(file: UploadFile):
client.upload_fileobj(
Fileobj=file.file, # streams in chunks, no full read
Bucket=BUCKET,
Key=f"uploads/{file.filename}",
ExtraArgs={"ContentType": file.content_type},
)
return {"key": f"uploads/{file.filename}"}Ikke blokker hendelsesløkken
boto3 er synkront. Hvis De kaller upload_fileobj direkte inne i et async def-endepunkt, blokkeres hendelsesløkken mens bytene sendes til S3, og alle andre forespørsler på arbeideren stopper opp.
Flytt det blokkerende kallet til en trådpool med run_in_threadpool (Starlette) eller asyncio.to_thread. Da forblir hendelsesløkken ledig til å betjene andre tilkoblinger.
from fastapi import FastAPI, UploadFile
from fastapi.concurrency import run_in_threadpool
app = FastAPI()
BUCKET = "user-media"
@app.post("/upload")
async def upload(file: UploadFile):
key = f"uploads/{file.filename}"
await run_in_threadpool(
client.upload_fileobj, file.file, BUCKET, key,
{"ContentType": file.content_type},
)
return {"key": key}Forhåndssignerte URL-er: La klientene snakke direkte med S3
Strømming gjennom API-et bruker fortsatt båndbredden og CPU-en Deres to ganger (klient→API, API→S3). Det mest skalerbare mønsteret fjerner API-et helt fra dataflyten ved å bruke en forhåndssignert URL.
En forhåndssignert URL er en midlertidig, signert lenke som gir tillatelse til én bestemt operasjon (PUT eller GET) på ett objekt, og som utløper etter N sekunder. Klienten laster opp direkte til S3; API-et Deres signerer bare forespørselen.
- API-et forblir tilstandsløst og lite — det håndterer aldri bytene.
- Legitimasjonen forlater aldri serveren; signaturen inneholder tillatelsen.
Generere en forhåndssignert PUT-URL
Bruk generate_presigned_url med klientmetoden put_object for å opprette en opplastingslenke. Endepunktet returnerer URL-en samt den endelige objektnøkkelen; nettleseren sender deretter en vanlig HTTP-PUT til URL-en med filinnholdet.
Angi en kort ExpiresIn (for eksempel 300–900 sekunder) — akkurat lenge nok til å starte opplastingen.
import uuid
from fastapi import FastAPI
app = FastAPI()
BUCKET = "user-media"
@app.post("/uploads/presign")
def presign_put(filename: str, content_type: str):
key = f"uploads/{uuid.uuid4()}-{filename}"
url = client.generate_presigned_url(
ClientMethod="put_object",
Params={"Bucket": BUCKET, "Key": key, "ContentType": content_type},
ExpiresIn=600,
)
return {"upload_url": url, "key": key}Opplastingsflyten på klientsiden
Med en forhåndssignert PUT-URL laster nettleseren opp med én enkelt forespørsel — ikke et multipart-skjema, bare det rå innholdet. Flyten er:
- 1. Klienten ber API-et Deres om en forhåndssignert URL (sender filnavn + innholdstype).
- 2. API-et returnerer
upload_urlog den endeligekey. - 3. Klienten utfører
PUT upload_urlmed filbytene og den tilsvarendeContent-Type-headeren. - 4. Klienten varsler API-et Deres om
key, slik at De kan lagre den i databasen.
Content-Type på PUT-forespørselen må samsvare med den De signerte, ellers returnerer S3 403.
// Browser-side (illustrative)
const { upload_url, key } = await api.presign(file.name, file.type);
await fetch(upload_url, {
method: "PUT",
headers: { "Content-Type": file.type },
body: file,
});
await api.confirm(key);Levere private filer med forhåndssignerte GET-URL-er
Gjør buckets private som standard. Hvis en bruker skal laste ned eller vise en fil, genererer De en kortvarig forhåndssignert get_object-URL ved behov i stedet for å gjøre objektet offentlig.
Da styres tilgangen av autentiseringen i API-et Deres: kontroller at brukeren eier filen, og signer deretter en URL som er gyldig i noen minutter. Bygg den inn i en <img>-src-attributt eller returner den som en omdirigering.
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
app = FastAPI()
BUCKET = "user-media"
@app.get("/files/{key:path}")
def download(key: str):
url = client.generate_presigned_url(
ClientMethod="get_object",
Params={"Bucket": BUCKET, "Key": key},
ExpiresIn=300,
)
return RedirectResponse(url)Begrense opplastinger med forhåndssignert POST
En forhåndssignert PUT-URL kan ikke begrense filstørrelsen — en ondsinnet klient kan laste opp en fil på 50 GB. Når De trenger serverhåndhevede begrensninger, bør De i stedet bruke generate_presigned_post.
Den returnerer en URL og skjemafeltet fields, og lar Dem legge til betingelser som content-length-range og en nøyaktig innholdstype. S3 avviser opplastingen på serversiden hvis bytene bryter med policyen.
from fastapi import FastAPI
app = FastAPI()
BUCKET = "user-media"
@app.post("/uploads/presign-post")
def presign_post(key: str, content_type: str):
return client.generate_presigned_post(
Bucket=BUCKET,
Key=key,
Fields={"Content-Type": content_type},
Conditions=[
{"Content-Type": content_type},
["content-length-range", 1, 10 * 1024 * 1024], # max 10 MB
],
ExpiresIn=600,
)En gjenbrukbar nøkkelbygger
Objektnøkler bør være kollisjonssikre, organiserte, og De bør aldri stole på klientens rå filnavn (som kan inneholde ../ eller uvanlige tegn). En liten hjelpefunksjon samler denne logikken på ett sted og er ren Python — enkelt å enhetsteste.
En god nøkkel inneholder et logisk prefiks (eier, kategori), en UUID for unikhet og en renset filendelse.
import re
import uuid
def build_key(user_id: int, filename: str) -> str:
ext = filename.rsplit(".", 1)[-1].lower() if "." in filename else "bin"
ext = re.sub(r"[^a-z0-9]", "", ext)[:8] or "bin"
return f"users/{user_id}/{uuid.uuid4().hex}.{ext}"
print(build_key(42, "My Vacation.JPG"))
print(build_key(7, "../../etc/passwd"))
print(build_key(1, "noext"))Hurtigsjekk: Velge det skalerbare mønsteret
De bygger et endepunkt som lar brukere laste opp store videoer (opptil 2 GB). De vil at FastAPI-tjenesten skal forbli tilstandsløs og unngå å sende filbytene gjennom API-serveren i det hele tatt. Hvilken metode passer best?
Oppsummering: Tilstandsløse medier i stor skala
De har nå et komplett verktøysett for å flytte lagringen til S3-kompatible bucketer:
- Én klient, mange leverandører —
boto3med enendpoint_urlkan brukes mot S3, MinIO, R2 og Spaces. - Bufre aldri hele filer — hvis byte må gå gjennom API-et, bruk
upload_fileobjog flytt det til en trådpool medrun_in_threadpool, slik at hendelsesløkken forblir ledig. - Foretrekk forhåndssignerte URL-er — klientene utfører PUT/GET direkte mot S3; API-et signerer bare og forblir tilstandsløst.
- Håndhev begrensninger med
generate_presigned_postog encontent-length-range-betingelse. - Hold bucketer private og lever filer via kortvarige forhåndssignerte GET-lenker som beskyttes av autentiseringen Deres.
- Rens nøkler — bruk UUID-baserte nøkler med prefiks, og stol aldri på rå filnavn.
Resultatet er et API som håndterer opplastinger på 2 GB eller 2 KB med samme begrensede ressursbruk.
Lær deg Bootcamp i backendutvikling med FastAPI med en AI-veileder – gratis
Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.
- Kurs
- 21
- Leksjoner
- 84
Ofte stilte spørsmål
Er leksjonen «Avlasting av lagring til S3-kompatible bøtter» gratis?
Ja – du kan lese valgfritt 3 av leksjonene i læringsstien Bootcamp i backendutvikling med FastAPI, inkludert «Avlasting av lagring til S3-kompatible bøtter», gratis i sin helhet her på nettet. Deretter låser CoddyKit PRO opp alle leksjoner, samt interaktiv øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Kurset i Bootcamp i backendutvikling med FastAPI inneholder totalt 4 leksjoner.
Hva lærer jeg i «Avlasting av lagring til S3-kompatible bøtter»?
Strøm opplastinger direkte til S3/MinIO med forhåndssignerte URL-er, slik at API-et forblir tilstandsløst og skalerbart. Du øver på Bootcamp i backendutvikling med FastAPI med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.
Trenger jeg erfaring for å begynne med Bootcamp i backendutvikling med FastAPI?
Ingen tidligere erfaring er nødvendig. Bootcamp i backendutvikling med FastAPI på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 3 av 4.
Hvor lang tid tar leksjonen «Avlasting av lagring til S3-kompatible bøtter»?
De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.
Kan jeg skrive og kjøre kode i denne Bootcamp i backendutvikling med FastAPI-leksjonen?
Ja. Alle Bootcamp i backendutvikling med FastAPI-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.
Alle leksjonene i dette kurset
- Multipart-opplastinger og innholdsvalidering
- Strømmende svar og range-forespørsler
- Avlasting av lagring til S3-kompatible bøtter
- Asynkron bilde- og dokumenttransformasjon