Atskleiskite „FastAPI“ galią efektyviam „multipart form“ failų įkėlimui. Šis išsamus vadovas apima geriausią praktiką, klaidų apdorojimą ir pažangias technikas pasauliniams kūrėjams.
„FastAPI“ failų įkėlimo įvaldymas: išsamus „Multipart Form“ apdorojimo vadovas
Šiuolaikinėse interneto programose galimybė apdoroti failų įkėlimus yra pagrindinis reikalavimas. Nesvarbu, ar tai būtų vartotojų pateikiamos profilio nuotraukos, apdorojami dokumentai ar bendrinama medija, patikimi ir efektyvūs failų įkėlimo mechanizmai yra labai svarbūs. „FastAPI“, didelio našumo „Python“ interneto framework'as, puikiai tinka šioje srityje, siūlydamas supaprastintus būdus valdyti „multipart form“ duomenis, kurie yra standartas siunčiant failus per HTTP. Šis išsamus vadovas supažindins jus su „FastAPI“ failų įkėlimo subtilybėmis, nuo pagrindinio įgyvendinimo iki pažangių aspektų, užtikrinant, kad galėtumėte užtikrintai kurti galingas ir keičiamo dydžio API pasaulinei auditorijai.
„Multipart Form“ duomenų supratimas
Prieš pasineriant į „FastAPI“ įgyvendinimą, būtina suprasti, kas yra „multipart form“ duomenys. Kai interneto naršyklė pateikia formą, kurioje yra failų, ji paprastai naudoja enctype="multipart/form-data" atributą. Šis kodavimo tipas suskaido formos pateikimą į kelias dalis, kurių kiekviena turi savo turinio tipą ir informaciją apie dispoziciją. Tai leidžia perduoti skirtingų tipų duomenis per vieną HTTP užklausą, įskaitant teksto laukus, ne tekstinius laukus ir dvejetainius failus.
Kiekviena „multipart“ užklausos dalis susideda iš:
- „Content-Disposition“ antraštė: nurodo formos lauko pavadinimą (
name) ir, failams, originalų failo pavadinimą (filename). - „Content-Type“ antraštė: nurodo dalies MIME tipą (pvz.,
text/plain,image/jpeg). - Kūnas: faktiniai tos dalies duomenys.
„FastAPI“ požiūris į failų įkėlimus
„FastAPI“ naudoja „Python“ standartinę biblioteką ir sklandžiai integruojasi su „Pydantic“, kad patvirtintų duomenis. Failų įkėlimui jis naudoja UploadFile tipą iš fastapi modulio. Ši klasė suteikia patogią ir saugią sąsają norint pasiekti įkeltus failų duomenis.
Pagrindinis failų įkėlimo įgyvendinimas
Pradėkime nuo paprasto pavyzdžio, kaip sukurti galinį tašką „FastAPI“, kuris priima vieną failo įkėlimą. Naudosime funkciją File iš fastapi, kad deklaruotume failo parametrą.
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: UploadFile):
return {"filename": file.filename, "content_type": file.content_type}
Šiame pavyzdyje:
- Importuojame
FastAPI,FileirUploadFile. - Galinis taškas
/files/apibrėžiamas kaipPOSTužklausa. - Parametras
fileyra pažymėtasUploadFile, nurodant, kad jis tikisi failo įkėlimo. - Galinio taško funkcijos viduje galime pasiekti įkelto failo savybes, tokias kaip
filenameircontent_type.
Kai klientas siunčia POST užklausą į /files/ su pridėtu failu (paprastai per formą su enctype="multipart/form-data"), „FastAPI“ automatiškai apdoros analizę ir pateiks UploadFile objektą. Tada galite sąveikauti su šiuo objektu.
Įkeltų failų išsaugojimas
Dažnai reikės išsaugoti įkeltą failą diske arba apdoroti jo turinį. Objektas UploadFile pateikia metodus, skirtus tam:
read(): nuskaito visą failo turinį į atmintį kaip baitus. Naudokite tai mažesniems failams.write(content: bytes): įrašo baitus į failą.seek(offset: int): pakeičia esamą failo poziciją.close(): uždaro failą.
Svarbu apdoroti failų operacijas asinchroniškai, ypač kai dirbate su dideliais failais arba su I/O susijusiomis užduotimis. „FastAPI“ UploadFile palaiko asinchronines operacijas.
from fastapi import FastAPI, File, UploadFile
import shutil
app = FastAPI()
@app.post("/files/save/")
async def save_file(file: UploadFile = File(...)):
file_location = f"./uploads/{file.filename}"
with open(file_location, "wb+") as file_object:
file_object.write(await file.read())
return {"info": f"file '{file.filename}' saved at '{file_location}'"}
Šiame patobulintame pavyzdyje:
- Naudojame
File(...), kad nurodytume, jog šis parametras yra būtinas. - Nurodome vietinį kelią, kur bus išsaugotas failas. Įsitikinkite, kad egzistuoja
uploadskatalogas. - Atidarome paskirties failą dvejetainio įrašymo režimu (`"wb+"`).
- Asinchroniškai nuskaitome įkelto failo turinį naudodami
await file.read()ir tada įrašome jį į vietinį failą.
Pastaba: Viso failo skaitymas į atmintį su await file.read() gali būti problematiškas labai dideliems failams. Tokiais atvejais apsvarstykite galimybę transliuoti failo turinį.
Failų turinio transliacija
Dideliems failams viso turinio skaitymas į atmintį gali sukelti per didelį atminties suvartojimą ir galimas atminties trūkumo klaidas. Daugiau atminties efektyvumo reikalaujantis metodas yra failo transliacija gabalais. Funkcija shutil.copyfileobj puikiai tinka tam, bet mes turime ją pritaikyti asinchroninėms operacijoms.
from fastapi import FastAPI, File, UploadFile
import aiofiles # Install using: pip install aiofiles
app = FastAPI()
@app.post("/files/stream/")
async def stream_file(file: UploadFile = File(...)):
file_location = f"./uploads/{file.filename}"
async with aiofiles.open(file_location, "wb") as out_file:
content = await file.read()
await out_file.write(content)
return {"info": f"file '{file.filename}' streamed and saved at '{file_location}'"}
Su aiofiles galime efektyviai transliuoti įkelto failo turinį į paskirties failą neįkeliant viso failo į atmintį vienu metu. await file.read() šiame kontekste vis dar nuskaito visą failą, bet aiofiles efektyviau apdoroja įrašymą. Norėdami tikrai transliuoti gabalą po gabalo su UploadFile, paprastai iteruotumėte per await file.read(chunk_size), bet aiofiles.open ir await out_file.write(content) yra įprastas ir našus modelis išsaugoti.
Daugiau eksplicitinis transliacijos metodas naudojant skaidymą į gabalus:
from fastapi import FastAPI, File, UploadFile
import aiofiles
app = FastAPI()
CHUNK_SIZE = 1024 * 1024 # 1MB chunk size
@app.post("/files/chunked_stream/")
async def chunked_stream_file(file: UploadFile = File(...)):
file_location = f"./uploads/{file.filename}"
async with aiofiles.open(file_location, "wb") as out_file:
while content := await file.read(CHUNK_SIZE):
await out_file.write(content)
return {"info": f"file '{file.filename}' chunked streamed and saved at '{file_location}'"}
Šis `chunked_stream_file` galinis taškas nuskaito failą 1 MB gabalais ir įrašo kiekvieną gabalą į išvesties failą. Tai yra efektyviausias būdas valdyti potencialiai labai didelius failus.
Kelių failų įkėlimo apdorojimas
Interneto programoms dažnai reikia, kad vartotojai vienu metu įkeltų kelis failus. „FastAPI“ tai atlieka paprastai.
Failų sąrašo įkėlimas
Galite priimti failų sąrašą, anotuodami savo parametrą failų UploadFile sąrašu.
from fastapi import FastAPI, File, UploadFile, Form
from typing import List
app = FastAPI()
@app.post("/files/multiple/")
async def create_multiple_files(
files: List[UploadFile] = File(...)
):
results = []
for file in files:
# Process each file, e.g., save it
file_location = f"./uploads/{file.filename}"
with open(file_location, "wb+") as file_object:
file_object.write(await file.read())
results.append({"filename": file.filename, "content_type": file.content_type, "saved_at": file_location})
return {"files_processed": results}
Šiame scenarijuje klientas turi išsiųsti kelias dalis su tuo pačiu formos lauko pavadinimu (pvz., `files`). „FastAPI“ surinks juos į „Python“ UploadFile objektų sąrašą.
Failų ir kitų formos duomenų maišymas
Dažnai pasitaiko formų, kuriose yra tiek failų laukų, tiek įprastų teksto laukų. „FastAPI“ tai apdoroja leisdamas deklaruoti kitus parametrus naudojant standartines tipo anotacijas, kartu su Form formos laukams, kurie nėra failai.
from fastapi import FastAPI, File, UploadFile, Form
from typing import List
app = FastAPI()
@app.post("/files/mixed/")
async def upload_mixed_data(
description: str = Form(...),
files: List[UploadFile] = File(...) # Accepts multiple files with the name 'files'
):
results = []
for file in files:
# Process each file
file_location = f"./uploads/{file.filename}"
with open(file_location, "wb+") as file_object:
file_object.write(await file.read())
results.append({"filename": file.filename, "content_type": file.content_type, "saved_at": file_location})
return {
"description": description,
"files_processed": results
}
Naudodami tokius įrankius kaip „Swagger UI“ arba „Postman“, description nurodysite kaip įprastą formos lauką, o tada pridėsite kelias dalis laukui files, kiekviena su savo turinio tipu, nustatytu į atitinkamą vaizdo/dokumento tipą.
Pažangios funkcijos ir geriausia praktika
Be pagrindinio failų apdorojimo, kelios pažangios funkcijos ir geriausia praktika yra labai svarbios kuriant patikimas failų įkėlimo API.
Failo dydžio apribojimai
Leidimas neribotam failų įkėlimui gali sukelti paslaugos trikdymo atakas arba per didelį išteklių suvartojimą. Nors „FastAPI“ pagal numatytuosius nustatymus netaiko griežtų apribojimų sistemos lygiu, turėtumėte įdiegti patikrinimus:
- Programos lygiu: Patikrinkite failo dydį po to, kai jis buvo gautas, bet prieš apdorojant arba išsaugant.
- Interneto serverio/proxy lygiu: sukonfigūruokite savo interneto serverį (pvz., „Nginx“, „Uvicorn“ su darbuotojais), kad atmestų užklausas, viršijančias tam tikrą naudingosios apkrovos dydį.
Programos lygio dydžio patikrinimo pavyzdys:
from fastapi import FastAPI, File, UploadFile, HTTPException
app = FastAPI()
MAX_FILE_SIZE_MB = 10
MAX_FILE_SIZE_BYTES = MAX_FILE_SIZE_MB * 1024 * 1024
@app.post("/files/limited_size/")
async def upload_with_size_limit(file: UploadFile = File(...)):
if len(await file.read()) > MAX_FILE_SIZE_BYTES:
raise HTTPException(status_code=400, detail=f"File is too large. Maximum size is {MAX_FILE_SIZE_MB}MB.")
# Reset file pointer to read content again
await file.seek(0)
# Proceed with saving or processing the file
file_location = f"./uploads/{file.filename}"
with open(file_location, "wb+") as file_object:
file_object.write(await file.read())
return {"info": f"File '{file.filename}' uploaded successfully."}
Svarbu: Perskaitę failą, kad patikrintumėte jo dydį, turite naudoti await file.seek(0), kad iš naujo nustatytumėte failo žymeklį į pradžią, jei ketinate dar kartą perskaityti jo turinį (pvz., kad jį išsaugotumėte).
Leidžiami failų tipai (MIME tipai)
Įkėlimų apribojimas konkrečiais failų tipais padidina saugumą ir užtikrina duomenų vientisumą. Galite patikrinti atributo content_type objektą UploadFile.
from fastapi import FastAPI, File, UploadFile, HTTPException
app = FastAPI()
ALLOWED_FILE_TYPES = {"image/jpeg", "image/png", "application/pdf"}
@app.post("/files/restricted_types/")
async def upload_restricted_types(file: UploadFile = File(...)):
if file.content_type not in ALLOWED_FILE_TYPES:
raise HTTPException(status_code=400, detail=f"Unsupported file type: {file.content_type}. Allowed types are: {', '.join(ALLOWED_FILE_TYPES)}")
# Proceed with saving or processing the file
file_location = f"./uploads/{file.filename}"
with open(file_location, "wb+") as file_object:
file_object.write(await file.read())
return {"info": f"File '{file.filename}' uploaded successfully and is of an allowed type."}
Norėdami patikrinti tipą patikimiau, ypač vaizdams, galite apsvarstyti galimybę naudoti tokias bibliotekas kaip „Pillow“, kad patikrintumėte faktinį failo turinį, nes MIME tipai kartais gali būti suklastoti.
Klaidų apdorojimas ir vartotojų atsiliepimai
Pateikite aiškius ir veiksmingus klaidų pranešimus vartotojui. Naudokite „FastAPI“ HTTPException standartiniams HTTP klaidų atsakymams.
- Failas nerastas/trūksta: jei nebuvo išsiųstas būtinas failo parametras.
- Viršytas failo dydis: kaip parodyta dydžio apribojimo pavyzdyje.
- Netinkamas failo tipas: kaip parodyta tipo apribojimo pavyzdyje.
- Serverio klaidos: problemoms, kylančioms išsaugant arba apdorojant failą (pvz., diskas pilnas, leidimo klaidos).
Saugumo aspektai
Failų įkėlimas kelia saugumo riziką:
- Kenksmingi failai: Vykdomųjų failų (
.exe,.sh) arba scenarijų įkėlimas, užmaskuotas kaip kiti failų tipai. Visada patvirtinkite failų tipus ir apsvarstykite galimybę nuskaityti įkeltus failus dėl kenkėjiškų programų. - Kelio traversal: Sanitizuokite failų pavadinimus, kad užkirstumėte kelią užpuolikams įkelti failus į nenumatytus katalogus (pvz., naudojant tokius failų pavadinimus kaip
../../etc/passwd). „FastAPI“UploadFileapdoroja pagrindinį failo pavadinimo sanitizavimą, tačiau papildoma priežiūra yra protinga. - Paslaugos trikdymas: Įdiekite failų dydžio apribojimus ir galbūt įkėlimo galinių taškų dažnio apribojimą.
- Tarp svetainių scenarijų vykdymas (XSS): Jei failų pavadinimus ar failų turinį tiesiogiai rodote tinklalapyje, įsitikinkite, kad jie yra tinkamai išvengti, kad būtų išvengta XSS atakų.
Geriausia praktika: Išsaugokite įkeltus failus už savo interneto serverio dokumentų šaknies ribų ir patiekite juos per tam skirtą galinį tašką su atitinkamais prieigos valdikliais arba naudokite turinio pristatymo tinklą (CDN).
„Pydantic“ modelių naudojimas su failų įkėlimais
Nors UploadFile yra pagrindinis failų tipas, galite integruoti failų įkėlimus į „Pydantic“ modelius, kad gautumėte sudėtingesnes duomenų struktūras. Tačiau tiesioginiai failų įkėlimo laukai standartiniuose „Pydantic“ modeliuose iš esmės nepalaikomi „multipart“ formoms. Vietoj to, failą paprastai gaunate kaip atskirą parametrą ir tada galite apdoroti jį į formatą, kurį gali saugoti arba patvirtinti „Pydantic“ modelis.
Dažnas modelis yra turėti „Pydantic“ modelį metaduomenims ir tada gauti failą atskirai:
from fastapi import FastAPI, File, UploadFile, Form
from pydantic import BaseModel
from typing import Optional
class UploadMetadata(BaseModel):
title: str
description: Optional[str] = None
app = FastAPI()
@app.post("/files/model_metadata/")
async def upload_with_metadata(
metadata: str = Form(...), # Receive metadata as a JSON string
file: UploadFile = File(...)
):
import json
try:
metadata_obj = UploadMetadata(**json.loads(metadata))
except json.JSONDecodeError:
raise HTTPException(status_code=400, detail="Invalid JSON format for metadata")
except Exception as e:
raise HTTPException(status_code=400, detail=f"Error parsing metadata: {e}")
# Now you have metadata_obj and file
# Proceed with saving file and using metadata
file_location = f"./uploads/{file.filename}"
with open(file_location, "wb+") as file_object:
file_object.write(await file.read())
return {
"message": "File uploaded successfully with metadata",
"metadata": metadata_obj,
"filename": file.filename
}
Pagal šį modelį klientas siunčia metaduomenis kaip JSON eilutę formos lauke (pvz., metadata), o failą - kaip atskirą „multipart“ dalį. Tada serveris išanalizuoja JSON eilutę į „Pydantic“ objektą.
Didelių failų įkėlimas ir skaidymas į gabalus
Labai dideliems failams (pvz., gigabaitams) net transliacija gali pasiekti interneto serverio arba kliento pusės apribojimus. Pažangesnė technika yra įkėlimas gabalais, kai klientas suskaido failą į mažesnius gabalus ir įkelia juos nuosekliai arba lygiagrečiai. Tada serveris surenka šiuos gabalus. Paprastai tam reikia pasirinktinės kliento pusės logikos ir serverio galinio taško, skirto gabalų valdymui (pvz., gabalų identifikavimas, laikina saugykla ir galutinis surinkimas).
Nors „FastAPI“ nepateikia įmontuoto palaikymo kliento inicijuotiems įkėlimams gabalais, galite įdiegti šią logiką savo „FastAPI“ galiniuose taškuose. Tai apima galinių taškų kūrimą, kurie:
- Gaukite atskirus failų gabalus.
- Laikykite šiuos gabalus laikinai, galbūt su metaduomenimis, nurodančiais jų tvarką ir bendrą gabalų skaičių.
- Pateikite galinį tašką arba mechanizmą, kuris signalizuoja, kada visi gabalai buvo įkelti, suaktyvinant surinkimo procesą.
Tai yra sudėtingesnis įsipareigojimas ir dažnai apima „JavaScript“ bibliotekas kliento pusėje.
Internacionalizavimo ir globalizacijos aspektai
Kuriant API pasaulinei auditorijai, failų įkėlimams reikia ypatingo dėmesio:
- Failų pavadinimai: Vartotojai visame pasaulyje failų pavadinimuose gali naudoti ne ASCII simbolius (pvz., akcentus, ideogramas). Užtikrinkite, kad jūsų sistema teisingai apdorotų ir saugotų šiuos failų pavadinimus. UTF-8 kodavimas paprastai yra standartinis, tačiau gilus suderinamumas gali reikalauti kruopštaus kodavimo/dekodavimo ir sanitizavimo.
- Failų dydžio vienetai: Nors MB ir GB yra įprasti, atsižvelkite į tai, kaip vartotojai suvokia failų dydžius. Svarbu rodyti apribojimus patogiu vartotojui būdu.
- Turinio tipai: Vartotojai gali įkelti failus su mažiau paplitusiais MIME tipais. Įsitikinkite, kad jūsų leidžiamų tipų sąrašas yra išsamus arba pakankamai lankstus jūsų naudojimo atvejui.
- Regioniniai reglamentai: Žinokite duomenų gyvenamosios vietos įstatymus ir reglamentus skirtingose šalyse. Norint saugoti įkeltus failus, gali reikėti laikytis šių taisyklių.
- Vartotojo sąsaja: Kliento pusės sąsaja, skirta failų įkėlimui, turėtų būti intuityvi ir palaikyti vartotojo kalbą ir lokalę.
Įrankiai ir bibliotekos bandymui
Bandymų failų įkėlimo galiniai taškai yra labai svarbūs. Štai keletas įprastų įrankių:
- „Swagger UI“ (interaktyvi API dokumentacija): „FastAPI“ automatiškai generuoja „Swagger UI“ dokumentaciją. Galite tiesiogiai išbandyti failų įkėlimus iš naršyklės sąsajos. Ieškokite failo įvesties lauko ir spustelėkite mygtuką „Choose File“.
- „Postman“: populiarus API kūrimo ir testavimo įrankis. Norėdami išsiųsti failo įkėlimo užklausą:
- Nustatykite užklausos metodą į POST.
- Įveskite savo API galinio taško URL.
- Eikite į skirtuką „Body“.
- Pasirinkite „form-data“ kaip tipą.
- Raktų ir reikšmių porose įveskite savo failo parametro pavadinimą (pvz.,
file). - Pakeiskite tipą iš „Text“ į „File“.
- Spustelėkite „Choose Files“, kad pasirinktumėte failą iš savo vietinės sistemos.
- Jei turite kitų formos laukų, pridėkite juos panašiai, palikdami jų tipą kaip „Text“.
- Išsiųskite užklausą.
- cURL: komandinės eilutės įrankis HTTP užklausoms atlikti.
- Vienam failui:
curl -X POST -F "file=@/path/to/your/local/file.txt" http://localhost:8000/files/ - Keliems failams:
curl -X POST -F "files=@/path/to/file1.txt" -F "files=@/path/to/file2.png" http://localhost:8000/files/multiple/ - Mišriems duomenims:
curl -X POST -F "description=My description" -F "files=@/path/to/file.txt" http://localhost:8000/files/mixed/ - „Python“ biblioteka `requests`: programiniam testavimui.
import requests
url = "http://localhost:8000/files/save/"
files = {'file': open('/path/to/your/local/file.txt', 'rb')}
response = requests.post(url, files=files)
print(response.json())
# For multiple files
url_multiple = "http://localhost:8000/files/multiple/"
files_multiple = {
'files': [('file1.txt', open('/path/to/file1.txt', 'rb')),
('image.png', open('/path/to/image.png', 'rb'))]
}
response_multiple = requests.post(url_multiple, files=files_multiple)
print(response_multiple.json())
# For mixed data
url_mixed = "http://localhost:8000/files/mixed/"
data = {'description': 'Test description'}
files_mixed = {'files': open('/path/to/another_file.txt', 'rb')}
response_mixed = requests.post(url_mixed, data=data, files=files_mixed)
print(response_mixed.json())
Išvada
„FastAPI“ suteikia galingą, efektyvų ir intuityvų būdą valdyti „multipart“ failų įkėlimus. Naudodami UploadFile tipą ir asinchroninį programavimą, kūrėjai gali kurti patikimas API, kurios sklandžiai integruoja failų apdorojimo galimybes. Nepamirškite teikti pirmenybę saugumui, įgyvendinti tinkamą klaidų apdorojimą ir atsižvelgti į pasaulinės vartotojų bazės poreikius, atsižvelgiant į tokius aspektus kaip failų pavadinimų kodavimas ir atitiktis teisės aktams.
Nesvarbu, ar kuriate paprastą vaizdų bendrinimo paslaugą, ar sudėtingą dokumentų apdorojimo platformą, „FastAPI“ failų įkėlimo funkcijų įvaldymas bus reikšmingas turtas. Toliau tyrinėkite jo galimybes, įgyvendinkite geriausią praktiką ir teikite išskirtinę vartotojo patirtį savo tarptautinei auditorijai.