Asynkroninen kuvien ja asiakirjojen muunnos
Käsitelkää pikkukuvat, koon muuttaminen ja muotojen muunnokset taustatyöntekijöillä pyyntöjen viiveen pitämiseksi pienenä.
Asynkroninen kuvien ja asiakirjojen muunnos on ilmainen FastAPI-backend-kehityksen intensiivikurssi-oppitunti CoddyKitissä. Tämä on oppitunti 4/4. Voit lukea tästä oppimispolusta kokonaan mitkä tahansa 3 oppituntia ilmaiseksi — sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä käytännön harjoittelun sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Oppitunti kuuluu FastAPI-backend-kehityksen intensiivikurssi-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. FastAPI-backend-kehityksen intensiivikurssi-kurssilla on yhteensä 4 oppituntia.
Miksi mediatyö kannattaa siirtää muualle?
Kuvan koon muuttaminen tai PDF-tiedoston muuntaminen voi kestää satoja millisekunteja tai useita sekunteja. Jos teette työn pyyntökäsittelijän sisällä, asiakas joutuu odottamaan ja worker-prosessi on varattuna.
B2-tason FastAPI-palveluiden toimintamalli on seuraava:
- Hyväksykää lataus ja tallentakaa alkuperäinen tiedosto nopeasti
- Palauttakaa
202 Acceptedja tehtävän tunnus - Tehkää pikkukuvat, koon muuttaminen ja muodon muuntaminen taustatyöntekijässä
Näin pyyntöjen viive pysyy pienenä ja raskasta prosessorityötä voidaan skaalata itsenäisesti.
BackgroundTasks vai oikea tehtäväjono
FastAPI sisältää BackgroundTasks-ominaisuuden, joka suorittaa funktion vastauksen lähettämisen jälkeen, mutta edelleen saman prosessin sisällä. Se sopii edullisiin ja nopeasti suoritettaviin jatkotoimiin, kuten sähköpostin lähettämiseen tai lokin kirjoittamiseen.
Prosessoria raskaasti kuormittaviin mediamuunnoksiin se on väärä työkalu: se kilpailee tapahtumasilmukan kanssa ja keskeytyy, jos prosessi käynnistyy uudelleen. Käyttäkää mieluummin Redisillä tuettua erillistä tehtäväjonoa (Celery, RQ, Dramatiq tai arq), jotta työt säilyvät julkaisuista toiseen ja skaalautuvat vaakasuunnassa.
from fastapi import FastAPI, BackgroundTasks
app = FastAPI()
def log_upload(filename: str) -> None:
# cheap follow-up work only
print(f"received {filename}")
@app.post("/upload")
async def upload(background: BackgroundTasks):
background.add_task(log_upload, "photo.png")
return {"status": "accepted"}Hyväksykää nopeasti, käsitelkää myöhemmin
Päätepisteen pitäisi tehdä mahdollisimman vähän: tarkistaa tiedosto, siirtää se suoratoistona tallennustilaan, luoda tehtävärivi ja lisätä tehtävä jonoon. Huomaattehan, että luemme latauksen paloina, joten suuri tiedosto ei koskaan lataudu kokonaan muistiin.
await file.read(chunk)estää suuret muistin käytön piikit- Palautamme
job_id-tunnuksen, jota asiakas voi kysellä - Varsinainen muunnos tapahtuu kutsussa
process_image.delay(...)
import uuid, aiofiles
from fastapi import FastAPI, UploadFile, status
app = FastAPI()
@app.post("/images", status_code=status.HTTP_202_ACCEPTED)
async def create_image(file: UploadFile):
job_id = str(uuid.uuid4())
dest = f"/data/originals/{job_id}_{file.filename}"
async with aiofiles.open(dest, "wb") as out:
while chunk := await file.read(1024 * 1024):
await out.write(chunk)
process_image.delay(job_id, dest) # enqueue
return {"job_id": job_id, "status": "queued"}Pikkukuvien luominen Pillowlla
Pillow'n Image.thumbnail() muuttaa kokoa paikallaan säilyttäen kuvasuhteen eikä koskaan suurenna kuvaa. Se on oikea perusratkaisu pikkukuville, koska tulos mahtuu antamanne laatikon sisään.
Käyttäkää terävien pienennösten tekemiseen Image.LANCZOS -uudelleennäytteistystä ja kutsukaa img.convert("RGB") ennen JPEG-muodossa tallentamista, jotta alfakanavia sisältävät kuvat (PNG) eivät aiheuta kooderin kaatumista.
from PIL import Image
def make_thumbnail(src: str, dst: str, box=(256, 256)) -> None:
with Image.open(src) as img:
img = img.convert("RGB")
img.thumbnail(box, Image.LANCZOS)
img.save(dst, "JPEG", quality=85, optimize=True)
if __name__ == "__main__":
print("thumbnail helper ready")Celery-työntekijän tehtävä
Jokaisesta muunnoksesta tulee Celery-tehtävä. Tehtävä on tavallinen funktio, joka on koristeltu @app.task-koristelijalla; jono huolehtii uudelleenyrityksistä, kuittauksista ja samanaikaisuudesta.
- Luokaa useita kokoja yhdessä tehtävässä, jotta kuvan purkaminen voidaan hyödyntää mahdollisimman tehokkaasti
- Päivittäkää tehtävän tila valmistumisen jälkeen, jotta API voi ilmoittaa edistymisestä
- Asettakaa
autoretry_for, jotta tilapäiset I/O-virheet yritetään automaattisesti uudelleen
from celery import Celery
from PIL import Image
celery_app = Celery("media", broker="redis://localhost:6379/0")
SIZES = {"thumb": (256, 256), "medium": (1024, 1024)}
@celery_app.task(autoretry_for=(OSError,), retry_backoff=True, max_retries=3)
def process_image(job_id: str, src: str) -> dict:
outputs = {}
with Image.open(src) as base:
base = base.convert("RGB")
for name, box in SIZES.items():
img = base.copy()
img.thumbnail(box, Image.LANCZOS)
dst = f"/data/derived/{job_id}_{name}.jpg"
img.save(dst, "JPEG", quality=85, optimize=True)
outputs[name] = dst
return {"job_id": job_id, "outputs": outputs}Muodon muuntaminen: PNG ja WebP
WebP-muodon käyttäminen JPEG:n/PNG:n sijaan pienentää siirrettävän datan määrää 25–35 % vastaavalla laadulla, mikä vähentää kaistan käyttöä ja nopeuttaa sivujen latautumista.
Pillow muuntaa kuvan yksinkertaisesti valitsemalla tulostusmuodon kutsussa save(). Säilyttäkää myös kopio alkuperäisessä muodossa, koska jotkin vanhat asiakkaat eivät pysty purkamaan WebP-kuvia. Alla oleva esimerkki tuottaa yhdestä purkamisesta sekä JPEG- että WebP-version.
from PIL import Image
def to_jpeg_and_webp(src: str, stem: str) -> dict:
with Image.open(src) as img:
rgb = img.convert("RGB")
jpeg_path = f"{stem}.jpg"
webp_path = f"{stem}.webp"
rgb.save(jpeg_path, "JPEG", quality=85, optimize=True)
rgb.save(webp_path, "WEBP", quality=80, method=6)
return {"jpeg": jpeg_path, "webp": webp_path}
if __name__ == "__main__":
print(to_jpeg_and_webp.__name__)Tehtävän tilan seuranta
Asiakkaiden on tiedettävä, milloin niiden johdannaiset ovat valmiita. Tallentakaa pieni tilatietue (Redisiin tai tietokantaan), jonka avaimena on job_id, ja tarjotkaa kyselypäätepiste.
Elinkaaren tilat ovat yleensä queued -> processing -> done tai failed. Worker päivittää tietueen tehtävän alussa ja lopussa; API vain lukee sitä.
import json, redis
r = redis.Redis()
def set_status(job_id: str, state: str, **extra) -> None:
payload = {"state": state, **extra}
r.set(f"job:{job_id}", json.dumps(payload), ex=86400)
def get_status(job_id: str) -> dict | None:
raw = r.get(f"job:{job_id}")
return json.loads(raw) if raw else NoneKyselypäätepiste ja tulos-URL-osoitteet
Tilapäätepiste palauttaa nykyisen tilan ja tehtävän valmistuttua luotujen resurssien URL-osoitteet. Palauttakaa tuntemattomasta tehtävästä 404 ja muussa tapauksessa tilan kanssa 200.
Yleinen parannus on palauttaa kullekin johdannaiselle esiallekirjoitettu S3-URL-osoite, jotta asiakas lataa tiedoston suoraan objektitallennuksesta eikä API:n kautta.
from fastapi import FastAPI, HTTPException
app = FastAPI()
@app.get("/images/{job_id}")
async def image_status(job_id: str):
status = get_status(job_id)
if status is None:
raise HTTPException(status_code=404, detail="job not found")
return {"job_id": job_id, **status}Tapahtumasilmukan pitäminen vapautettuna
Joskus joudutte kutsumaan estävää kirjastoa (Pillow'ta tai PDF-työkalua) asynkronisesta päätepisteestä myös workerin ulkopuolella. Sen suora kutsuminen estää tapahtumasilmukan ja pysäyttää kaikki samanaikaiset pyynnöt.
Siirtäkää suoritus säiepooliin käyttämällä asyncio.to_thread-funktiota (tai Starletten run_in_threadpool-funktiota). Useiden ytimien yli suoritettaviin prosessorisidonnaisiin eriin ProcessPoolExecutor ohittaa GIL:n rajoituksen. Suoritettava esimerkki näyttää suorituksen siirtämisen säikeeseen.
import asyncio, time
def blocking_resize(n: int) -> int:
time.sleep(0.1) # stand-in for Pillow work
return n * n
async def handle(n: int) -> int:
# runs blocking_resize in a worker thread, loop stays free
return await asyncio.to_thread(blocking_resize, n)
async def main() -> None:
results = await asyncio.gather(*(handle(i) for i in range(5)))
print(results)
if __name__ == "__main__":
asyncio.run(main())Asiakirjojen muuntaminen PDF-tiedostoiksi ja kuviksi
Asiakirjamuunnokset (DOCX PDF:ksi, PDF-sivu PNG-pikkukuvaksi) käynnistävät yleensä ulkoisia työkaluja, kuten LibreOfficea (soffice --headless) tai pdftoppm-ohjelmaa. Ne ovat raskaita ja hitaita, joten ne kuuluvat worker-tehtävään eivätkä koskaan pyyntöpolulle.
Suorittakaa ne aina aikakatkaisulla ja kerätkää virheet, koska ulkoiset muuntimet voivat jäädä jumiin virheellisen syötteen vuoksi.
import subprocess
def docx_to_pdf(src: str, out_dir: str) -> str:
subprocess.run(
["soffice", "--headless", "--convert-to", "pdf",
"--outdir", out_dir, src],
check=True, timeout=120,
)
return out_dir
@celery_app.task(autoretry_for=(subprocess.TimeoutExpired,), max_retries=2)
def convert_document(job_id: str, src: str) -> dict:
out = docx_to_pdf(src, "/data/derived")
set_status(job_id, "done", out_dir=out)
return {"job_id": job_id, "out_dir": out}Kelpuutus, rajoitukset ja siivous
Vahvistamaton media muodostaa tietoturvariskin. Suojatkaa käsittelyketju ennen raskaan työn aloittamista:
- Varmistakaa tyyppi sisällön perusteella (esimerkiksi
Image.open().verify()-funktion tai taikatavujen avulla), ei vain tiedostotunnisteen perusteella - Rajoittakaa mittoja purkupommikuvien torjumiseksi; asettakaa
Image.MAX_IMAGE_PIXELS - Valvokaa kokorajoituksia latauksen suoratoiston aikana
- Siivotkaa alkuperäiset tiedostot ja johdannaiset virheen jälkeen tai TTL-ajan umpeuduttua
Hylätkää virheellinen syöte ajoissa, jotta haitallinen tiedosto ei koskaan päädy workerille.
from PIL import Image, UnidentifiedImageError
Image.MAX_IMAGE_PIXELS = 50_000_000 # guard against decompression bombs
def is_safe_image(path: str) -> bool:
try:
with Image.open(path) as img:
img.verify() # checks integrity without full decode
return True
except (UnidentifiedImageError, OSError):
return FalsePikatarkistus
Testatkaa ymmärrystänne siitä, missä raskas mediatyö kuuluu suorittaa.
Kertaus
Opitte pitämään mediaa raskaasti käsittelevät FastAPI-päätepisteet nopeina:
- Hyväksykää nopeasti, käsitelkää myöhemmin: siirtäkää lataus suoratoistona tallennustilaan, palauttakaa
202ja tehtävän tunnus ja lisätkää työ jonoon - Käyttäkää oikeaa tehtäväjonoa (Celery/RQ/arq + Redis) prosessoria raskaasti kuormittaviin muunnoksiin; käyttäkää
BackgroundTasks-ominaisuutta vain kevyisiin jatkotoimiin - Tehkää muunnokset Pillowlla: käyttäkää
thumbnail()-funktiota kuvasuhteen säilyttäviin koon muutoksiin,convert("RGB")-funktiota ennen JPEG-tallennusta ja WebP:tä pienempään siirrettävään datamäärään - Asiakirjojen muuntaminen käynnistää LibreOfficen kaltaisia työkaluja aikakatkaisulla ja aina workerissä
- Älkää koskaan estäkö silmukkaa: siirtäkää yksittäiset estävät kutsut säikeeseen
asyncio.to_thread-funktion avulla - Suojatkaa syöte: varmistakaa tyyppi, rajoittakaa pikselimäärää ja kokoa sekä siivotkaa johdannaiset
Opi FastAPI-backend-kehityksen intensiivikurssi tekoälytuutorin avulla — ilmaiseksi
Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.
- Kurssit
- 21
- Oppitunnit
- 84
Usein kysytyt kysymykset
Onko oppitunti ”Asynkroninen kuvien ja asiakirjojen muunnos” ilmainen?
Kyllä — voit lukea täällä verkossa kokonaan ilmaiseksi mitkä tahansa FastAPI-backend-kehityksen intensiivikurssi-oppimispolun 3 oppituntia, myös oppitunnin “Asynkroninen kuvien ja asiakirjojen muunnos”. Sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä interaktiiviset harjoitukset sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. FastAPI-backend-kehityksen intensiivikurssi-kurssilla on yhteensä 4 oppituntia.
Mitä opin oppitunnilla ”Asynkroninen kuvien ja asiakirjojen muunnos”?
Käsitelkää pikkukuvat, koon muuttaminen ja muotojen muunnokset taustatyöntekijöillä pyyntöjen viiveen pitämiseksi pienenä. Harjoittelet FastAPI-backend-kehityksen intensiivikurssi-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.
Tarvitsenko kokemusta aloittaakseni FastAPI-backend-kehityksen intensiivikurssi-opiskelun?
Aiempi kokemus ei ole tarpeen. CoddyKitin FastAPI-backend-kehityksen intensiivikurssi-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 4/4.
Kuinka kauan ”Asynkroninen kuvien ja asiakirjojen muunnos”-oppitunnin suorittaminen kestää?
Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.
Voinko kirjoittaa ja suorittaa koodia tällä FastAPI-backend-kehityksen intensiivikurssi-oppitunnilla?
Kyllä. Jokainen FastAPI-backend-kehityksen intensiivikurssi-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.
Kaikki tämän kurssin oppitunnit
- Multipart-lataukset ja sisällön validointi
- Suoratoistovastaukset ja range-pyynnöt
- Tallennuksen siirtäminen S3-yhteensopiviin bucketeihin
- Asynkroninen kuvien ja asiakirjojen muunnos