å¹ççãªmultipartãã©ãŒã ãã¡ã€ã«ã¢ããããŒãã®ããã®FastAPIã®åãè§£ãæŸã¡ãŸãããããã®å æ¬çãªã¬ã€ãã¯ãã°ããŒãã«éçºè åãã®ãã¹ããã©ã¯ãã£ã¹ããšã©ãŒåŠçãé«åºŠãªãã¯ããã¯ãç¶²çŸ ããŠããŸãã
FastAPIãã¡ã€ã«ã¢ããããŒãããã¹ã¿ãŒããïŒMultipartãã©ãŒã åŠçã®è©³çް
çŸä»£ã®Webã¢ããªã±ãŒã·ã§ã³ã§ã¯ããã¡ã€ã«ã¢ããããŒããåŠçããèœåã¯åºæ¬çãªèŠä»¶ã§ãããŠãŒã¶ãŒããããã£ãŒã«åçãåŠçããããã¥ã¡ã³ãããŸãã¯å ±æããã¡ãã£ã¢ãéä¿¡ããå Žåã§ããå ç¢ã§å¹ççãªãã¡ã€ã«ã¢ããããŒãã¡ã«ããºã ãäžå¯æ¬ ã§ããé«ããã©ãŒãã³ã¹ãªPython Webãã¬ãŒã ã¯ãŒã¯ã§ããFastAPIã¯ããã®åéã§åªããŠãããHTTPçµç±ã§ãã¡ã€ã«ãéä¿¡ããããã®æšæºã§ããmultipartãã©ãŒã ããŒã¿ã管çããããã®åçåãããæ¹æ³ãæäŸããŠããŸãããã®å æ¬çãªã¬ã€ãã§ã¯ãåºæ¬çãªå®è£ ããé«åºŠãªèæ ®äºé ãŸã§ãFastAPIãã¡ã€ã«ã¢ããããŒãã®è€éãã詳ãã説æããã°ããŒãã«ãªãŒãã£ãšã³ã¹åãã®åŒ·åã§ã¹ã±ãŒã©ãã«ãªAPIãèªä¿¡ãæã£ãŠæ§ç¯ã§ããããã«ããŸãã
Multipartãã©ãŒã ããŒã¿ã®çè§£
FastAPIã®å®è£
ã«å
¥ãåã«ãmultipartãã©ãŒã ããŒã¿ãšã¯äœããçè§£ããããšãäžå¯æ¬ ã§ããWebãã©ãŠã¶ããã¡ã€ã«ãå«ããã©ãŒã ãéä¿¡ããå Žåãéåžžã¯enctype="multipart/form-data"屿§ã䜿çšããŸãããã®ãšã³ã³ãŒãã£ã³ã°ã¿ã€ãã¯ããã©ãŒã ã®éä¿¡ãè€æ°ã®éšåã«åå²ããããããã«ç¬èªã®ã³ã³ãã³ãã¿ã€ããšãã£ã¹ããžã·ã§ã³æ
å ±ãæãããŸããããã«ãããããã¹ããã£ãŒã«ããéããã¹ããã£ãŒã«ãããã€ããªãã¡ã€ã«ãªã©ãããŸããŸãªçš®é¡ã®ããŒã¿ãåäžã®HTTPãªã¯ãšã¹ãå
ã§éä¿¡ã§ããŸãã
multipartãªã¯ãšã¹ãã®åéšåã¯ã以äžã§æ§æãããŠããŸãã
- Content-DispositionããããŒïŒãã©ãŒã ãã£ãŒã«ãã®ååïŒ
nameïŒãšããã¡ã€ã«ã®å Žåã¯å ã®ãã¡ã€ã«åïŒfilenameïŒãæå®ããŸãã - Content-TypeããããŒïŒéšåã®MIMEã¿ã€ãïŒäŸïŒ
text/plainãimage/jpegïŒã瀺ããŸãã - BodyïŒãã®éšåã®å®éã®ããŒã¿ã
FastAPIã®ãã¡ã€ã«ã¢ããããŒããžã®ã¢ãããŒã
FastAPIã¯ãPythonã®æšæºã©ã€ãã©ãªã掻çšããããŒã¿æ€èšŒã®ããã«Pydanticãšã·ãŒã ã¬ã¹ã«çµ±åãããŠããŸãããã¡ã€ã«ã¢ããããŒãã®å Žåãfastapiã¢ãžã¥ãŒã«ããUploadFileã¿ã€ãã䜿çšããŸãããã®ã¯ã©ã¹ã¯ãã¢ããããŒãããããã¡ã€ã«ããŒã¿ã«ã¢ã¯ã»ã¹ããããã®äŸ¿å©ã§å®å
šãªã€ã³ã¿ãŒãã§ãŒã¹ãæäŸããŸãã
åºæ¬çãªãã¡ã€ã«ã¢ããããŒãã®å®è£
åäžã®ãã¡ã€ã«ã¢ããããŒããåãå
¥ããFastAPIã§ãšã³ããã€ã³ããäœæããç°¡åãªäŸããå§ããŸããããfastapiããFile颿°ã䜿çšããŠããã¡ã€ã«ãã©ã¡ãŒã¿ã宣èšããŸãã
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}
ãã®äŸã§ã¯ã
FastAPIãFileãããã³UploadFileãã€ã³ããŒãããŸãã- ãšã³ããã€ã³ã
/files/ãPOSTãªã¯ãšã¹ããšããŠå®çŸ©ãããŠããŸãã fileãã©ã¡ãŒã¿ã¯UploadFileã§æ³šéãä»ããããŠããããã¡ã€ã«ã¢ããããŒããæåŸ ããŠããããšã瀺ããŠããŸãã- ãšã³ããã€ã³ã颿°å
ã§ã¯ã
filenameãcontent_typeãªã©ãã¢ããããŒãããããã¡ã€ã«ã®ããããã£ã«ã¢ã¯ã»ã¹ã§ããŸãã
ã¯ã©ã€ã¢ã³ãããã¡ã€ã«ãæ·»ä»ããã/files/ã«POSTãªã¯ãšã¹ããéä¿¡ãããšïŒéåžžã¯enctype="multipart/form-data"ã®ãã©ãŒã ãä»ããŠïŒãFastAPIã¯èªåçã«è§£æãåŠçããUploadFileãªããžã§ã¯ããæäŸããŸããæ¬¡ã«ããã®ãªããžã§ã¯ããæäœã§ããŸãã
ã¢ããããŒãããããã¡ã€ã«ã®ä¿å
å€ãã®å Žåãã¢ããããŒãããããã¡ã€ã«ããã£ã¹ã¯ã«ä¿åãããããã®å
容ãåŠçãããããå¿
èŠããããŸããUploadFileãªããžã§ã¯ãã¯ããããè¡ãããã®ã¡ãœãããæäŸããŸãã
read()ïŒãã¡ã€ã«ã®ãã¹ãŠã®å 容ããã€ããšããŠã¡ã¢ãªã«èªã¿èŸŒã¿ãŸããããã䜿çšããã®ã¯ãããå°ãããã¡ã€ã«ã®å Žåã§ããwrite(content: bytes)ïŒãã€ãããã¡ã€ã«ã«æžã蟌ã¿ãŸããseek(offset: int)ïŒçŸåšã®ãã¡ã€ã«äœçœ®ã倿ŽããŸããclose()ïŒãã¡ã€ã«ãéããŸãã
ç¹ã«å€§ããªãã¡ã€ã«ãI/OããŠã³ãã¿ã¹ã¯ãæ±ãå Žåã¯ããã¡ã€ã«ã®æäœãéåæçã«åŠçããããšãéèŠã§ããFastAPIã®UploadFileã¯ãéåææäœããµããŒãããŠããŸãã
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}'"}
ãã®æ¡åŒµãããäŸã§ã¯ã
File(...)ã䜿çšããŠããã®ãã©ã¡ãŒã¿ãå¿ é ã§ããããšã瀺ããŸãã- ãã¡ã€ã«ãä¿åãããããŒã«ã«ãã¹ãæå®ããŸãã
uploadsãã£ã¬ã¯ããªãååšããããšã確èªããŠãã ããã - ãã€ããªæžã蟌ã¿ã¢ãŒãïŒ
"wb+"ïŒã§å®å ãã¡ã€ã«ãéããŸãã await file.read()ã䜿çšããŠãã¢ããããŒãããããã¡ã€ã«ã®å 容ãéåæçã«èªã¿èŸŒã¿ããããããŒã«ã«ãã¡ã€ã«ã«æžã蟌ã¿ãŸãã
泚ïŒawait file.read()ã§ãã¹ãŠã®ãã¡ã€ã«ãã¡ã¢ãªã«èªã¿èŸŒãããšã¯ãéåžžã«å€§ããªãã¡ã€ã«ã§ã¯åé¡ã«ãªãå¯èœæ§ããããŸãããã®ãããªã·ããªãªã§ã¯ããã¡ã€ã«ã®å
容ãã¹ããªãŒãã³ã°ããããšãæ€èšããŠãã ããã
ãã¡ã€ã«å 容ã®ã¹ããªãŒãã³ã°
倧ããªãã¡ã€ã«ã®å Žåããã¹ãŠã®å
容ãã¡ã¢ãªã«èªã¿èŸŒããšãé床ã®ã¡ã¢ãªæ¶è²»ãšæœåšçãªã¡ã¢ãªäžè¶³ãšã©ãŒãçºçããå¯èœæ§ããããŸããããã¡ã¢ãªå¹çã®é«ãã¢ãããŒãã¯ããã¡ã€ã«ããã£ã³ã¯ããšã«ã¹ããªãŒãã³ã°ããããšã§ããshutil.copyfileobj颿°ã¯ããã«åªããŠããŸãããéåææäœã«é©å¿ããå¿
èŠããããŸãã
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}'"}
aiofilesã䜿çšãããšãã¢ããããŒãããããã¡ã€ã«ã®å
容ããäžåºŠã«ãã¹ãŠã®ãã¡ã€ã«ãã¡ã¢ãªã«ããŒãããããšãªããå®å
ãã¡ã€ã«ã«å¹ççã«ã¹ããªãŒãã³ã°ã§ããŸãããã®ã³ã³ããã¹ãã®await file.read()ã¯ãŸã ãã¡ã€ã«å
šäœãèªã¿ãŸãããaiofilesã¯æžã蟌ã¿ãããå¹ççã«åŠçããŸããUploadFileã䜿çšããçã®ãã£ã³ã¯ããšã®ã¹ããªãŒãã³ã°ã«ã¯ãéåžžãawait file.read(chunk_size)ãå埩åŠçããŸãããaiofiles.openãšawait out_file.write(content)ã¯ãä¿åã®ããã®äžè¬çã§é«æ§èœãªãã¿ãŒã³ã§ãã
ãã£ã³ã¯ã䜿çšããããæç€ºçãªã¹ããªãŒãã³ã°ã¢ãããŒãïŒ
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}'"}
ãã®chunked_stream_fileãšã³ããã€ã³ãã¯ã1MBã®ãã£ã³ã¯ã§ãã¡ã€ã«ãèªã¿åããåãã£ã³ã¯ãåºåãã¡ã€ã«ã«æžã蟌ã¿ãŸããããã¯ãæœåšçã«éåžžã«å€§ããªãã¡ã€ã«ãåŠçããããã®æãã¡ã¢ãªå¹çã®é«ãæ¹æ³ã§ãã
è€æ°ã®ãã¡ã€ã«ã¢ããããŒãã®åŠç
Webã¢ããªã±ãŒã·ã§ã³ã§ã¯ãå€ãã®å Žåãè€æ°ã®ãã¡ã€ã«ãåæã«ã¢ããããŒãããå¿ èŠããããŸããFastAPIã¯ãããç°¡åã«è¡ããŸãã
ãã¡ã€ã«ã®ãªã¹ãã®ã¢ããããŒã
ãã©ã¡ãŒã¿ã«UploadFileã®ãªã¹ãã§æ³šéãä»ããããšã§ããã¡ã€ã«ã®ãªã¹ããåãå
¥ããããšãã§ããŸãã
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}
ãã®ã·ããªãªã§ã¯ãã¯ã©ã€ã¢ã³ãã¯åããã©ãŒã ãã£ãŒã«ãåïŒäŸïŒfilesïŒã§è€æ°ã®éšåãéä¿¡ããå¿
èŠããããŸããFastAPIã¯ããããUploadFileãªããžã§ã¯ãã®Pythonãªã¹ãã«åéããŸãã
ãã¡ã€ã«ãšãã®ä»ã®ãã©ãŒã ããŒã¿ã®æ··å
ãã¡ã€ã«ãã£ãŒã«ããšéåžžã®ããã¹ããã£ãŒã«ãã®äž¡æ¹ãå«ããã©ãŒã ãæã€ããšã¯äžè¬çã§ããFastAPIã¯ãæšæºã®å泚éã«å ããŠããã¡ã€ã«ã§ã¯ãªããã©ãŒã ãã£ãŒã«ãã®Formã䜿çšããŠãä»ã®ãã©ã¡ãŒã¿ã宣èšã§ããããã«ããããšã§ãããåŠçããŸãã
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
}
Swagger UIãPostmanãªã©ã®ããŒã«ã䜿çšããå Žåãéåžžã®ãã©ãŒã ãã£ãŒã«ããšããŠdescriptionãæå®ããæ¬¡ã«filesãã£ãŒã«ãã«è€æ°ã®éšåã远å ããããããã«é©åãªç»å/ããã¥ã¡ã³ãã¿ã€ããèšå®ããŸãã
é«åºŠãªæ©èœãšãã¹ããã©ã¯ãã£ã¹
åºæ¬çãªãã¡ã€ã«åŠçãè¶ ããŠãå ç¢ãªãã¡ã€ã«ã¢ããããŒãAPIãæ§ç¯ããã«ã¯ãããã€ãã®é«åºŠãªæ©èœãšãã¹ããã©ã¯ãã£ã¹ãäžå¯æ¬ ã§ãã
ãã¡ã€ã«ãµã€ãºã®å¶é
ç¡å¶éã®ãã¡ã€ã«ã¢ããããŒããèš±å¯ãããšããµãŒãã¹æåŠæ»æãé床ã®ãªãœãŒã¹æ¶è²»ã«ã€ãªããå¯èœæ§ããããŸããFastAPIèªäœã¯ããã¬ãŒã ã¯ãŒã¯ã¬ãã«ã§ããã©ã«ãã§ããŒãå¶éãé©çšããŸãããããã§ãã¯ãå®è£ ããå¿ èŠããããŸãã
- ã¢ããªã±ãŒã·ã§ã³ã¬ãã«ïŒãã¡ã€ã«ãåãåã£ãåŸãåŠçãŸãã¯ä¿åããåã«ãã¡ã€ã«ãµã€ãºã確èªããŸãã
- WebãµãŒããŒ/ãããã·ã¬ãã«ïŒWebãµãŒããŒïŒäŸïŒNginxãworkerã䜿çšããUvicornïŒãèšå®ããŠãç¹å®ã®ãã€ããŒããµã€ãºãè¶ ãããªã¯ãšã¹ããæåŠããŸãã
ã¢ããªã±ãŒã·ã§ã³ã¬ãã«ã®ãµã€ãºãã§ãã¯ã®äŸïŒ
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."}
éèŠïŒãã¡ã€ã«ãµã€ãºã確èªããããã«ãã¡ã€ã«ãèªã¿åã£ãåŸããã®å
容ãå床èªã¿åãïŒäŸïŒä¿åããïŒå Žåã¯ãawait file.seek(0)ã䜿çšããŠãã¡ã€ã«ãã€ã³ã¿ãå
é ã«ãªã»ããããå¿
èŠããããŸãã
èš±å¯ããããã¡ã€ã«ã¿ã€ãïŒMIMEã¿ã€ãïŒ
ç¹å®ã®ãã¡ã€ã«ã¿ã€ããžã®ã¢ããããŒããå¶éãããšãã»ãã¥ãªãã£ã匷åãããããŒã¿ã®æŽåæ§ãä¿èšŒãããŸããUploadFileãªããžã§ã¯ãã®content_type屿§ã確èªã§ããŸãã
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."}
ããå ç¢ãªã¿ã€ããã§ãã¯ãè¡ãã«ã¯ãç¹ã«ç»åã®å ŽåãPillowã®ãããªã©ã€ãã©ãªã䜿çšããŠãã¡ã€ã«ã®å®éã®å å®¹ãæ€æ»ããããšãæ€èšããŠãã ãããMIMEã¿ã€ãã¯å Žåã«ãã£ãŠã¯ã¹ããŒãã£ã³ã°ãããå¯èœæ§ãããããã§ãã
ãšã©ãŒåŠçãšãŠãŒã¶ãŒãã£ãŒãããã¯
ãŠãŒã¶ãŒã«æç¢ºã§å®è¡å¯èœãªãšã©ãŒã¡ãã»ãŒãžãæäŸããŸããæšæºã®HTTPãšã©ãŒå¿çã«ã¯ãFastAPIã®HTTPExceptionã䜿çšããŸãã
- ãã¡ã€ã«ãèŠã€ãããªã/äžè¶³ïŒå¿ èŠãªãã¡ã€ã«ãã©ã¡ãŒã¿ãéä¿¡ãããŠããªãå Žåã
- ãã¡ã€ã«ãµã€ãºãè¶ éããŸããïŒãµã€ãºå¶éã®äŸã§ç€ºãããŠãããšããã
- ç¡å¹ãªãã¡ã€ã«ã¿ã€ãïŒã¿ã€ãå¶éã®äŸã§ç€ºãããŠãããšããã
- ãµãŒããŒãšã©ãŒïŒãã¡ã€ã«ã®ä¿åãŸãã¯åŠçäžã®åé¡ïŒäŸïŒãã£ã¹ã¯ããã£ã±ããæš©éãšã©ãŒïŒã
ã»ãã¥ãªãã£ã«é¢ããèæ ®äºé
ãã¡ã€ã«ã¢ããããŒãã¯ãã»ãã¥ãªãã£ãªã¹ã¯ããããããŸãã
- æªæã®ãããã¡ã€ã«ïŒå®è¡å¯èœãã¡ã€ã«ïŒ
.exeã.shïŒãŸãã¯ä»ã®ãã¡ã€ã«ã¿ã€ãã«åœè£ ãããã¹ã¯ãªãããã¢ããããŒãããŸããåžžã«ãã¡ã€ã«ã¿ã€ããæ€èšŒããã¢ããããŒãããããã¡ã€ã«ããã«ãŠã§ã¢ã®ã¹ãã£ã³ãæ€èšããŠãã ããã - ãã¹ã®èµ°æ»ïŒæ»æè
ãæå³ããªããã£ã¬ã¯ããªã«ãã¡ã€ã«ãã¢ããããŒãããã®ãé²ãããã«ããã¡ã€ã«åããµãã¿ã€ãºããŸãïŒäŸïŒ
../../etc/passwdã®ãããªãã¡ã€ã«åã䜿çšïŒãFastAPIã®UploadFileã¯åºæ¬çãªãã¡ã€ã«åã®ãµãã¿ã€ãºãåŠçããŸãããç¹å¥ãªæ³šæãå¿ èŠã§ãã - ãµãŒãã¹æåŠïŒãã¡ã€ã«ãµã€ãºã®å¶éãšãå Žåã«ãã£ãŠã¯ã¢ããããŒããšã³ããã€ã³ãã«å¯Ÿããã¬ãŒãå¶éãå®è£ ããŸãã
- ã¯ãã¹ãµã€ãã¹ã¯ãªããã£ã³ã°ïŒXSSïŒïŒãã¡ã€ã«åãŸãã¯ãã¡ã€ã«ã®å 容ãWebããŒãžã«çŽæ¥è¡šç€ºããå Žåã¯ãXSSæ»æãé²ãããã«é©åã«ãšã¹ã±ãŒããããŠããããšã確èªããŠãã ããã
ãã¹ããã©ã¯ãã£ã¹ïŒã¢ããããŒãããããã¡ã€ã«ãWebãµãŒããŒã®ããã¥ã¡ã³ãã«ãŒãã®å€éšã«ä¿åããå°çšã®ãšã³ããã€ã³ããä»ããŠé©åãªã¢ã¯ã»ã¹å¶åŸ¡ãè¡ã£ãŠæäŸããããContent Delivery NetworkïŒCDNïŒã䜿çšããŸãã
ãã¡ã€ã«ã¢ããããŒãã§ã®Pydanticã¢ãã«ã®äœ¿çš
UploadFileã¯ãã¡ã€ã«ã®äž»ãªã¿ã€ãã§ãããããè€éãªããŒã¿æ§é ã®ããã«ããã¡ã€ã«ã¢ããããŒããPydanticã¢ãã«ã«çµ±åã§ããŸãããã ããæšæºã®Pydanticã¢ãã«å
ã®çŽæ¥ãã¡ã€ã«ã¢ããããŒããã£ãŒã«ãã¯ãmultipartãã©ãŒã ã§ã¯ãã€ãã£ãã«ãµããŒããããŠããŸããã代ããã«ãéåžžã¯ãã¡ã€ã«ãå¥ã®ãã©ã¡ãŒã¿ãšããŠåä¿¡ããPydanticã¢ãã«ã§ä¿åãŸãã¯æ€èšŒã§ãã圢åŒã«åŠçããŸãã
äžè¬çãªãã¿ãŒã³ã¯ãã¡ã¿ããŒã¿çšã®Pydanticã¢ãã«ãæã¡ããã¡ã€ã«ãåå¥ã«åä¿¡ããããšã§ãã
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
}
ãã®ãã¿ãŒã³ã§ã¯ãã¯ã©ã€ã¢ã³ãã¯ãã©ãŒã ãã£ãŒã«ãïŒäŸïŒmetadataïŒå
ã§JSONæååãšããŠã¡ã¿ããŒã¿ãéä¿¡ãããã¡ã€ã«ãå¥ã®multipartéšåãšããŠéä¿¡ããŸããæ¬¡ã«ããµãŒããŒã¯JSONæååãPydanticãªããžã§ã¯ãã«è§£æããŸãã
倧ããªãã¡ã€ã«ã¢ããããŒããšãã£ã³ãã³ã°
éåžžã«å€§ããªãã¡ã€ã«ïŒäŸïŒã®ã¬ãã€ãïŒã®å Žåãã¹ããªãŒãã³ã°ã§ãããWebãµãŒããŒãŸãã¯ã¯ã©ã€ã¢ã³ãåŽã®å¶éã«éããå¯èœæ§ããããŸããããé«åºŠãªãã¯ããã¯ã¯ããã£ã³ã¯ãããã¢ããããŒãã§ãããã¯ã©ã€ã¢ã³ãããã¡ã€ã«ãããå°ããéšåã«åå²ããããããé çªã«ãŸãã¯äžŠåã«ã¢ããããŒãããŸããæ¬¡ã«ããµãŒããŒããããã®ãã£ã³ã¯ãåã¢ã»ã³ãã«ããŸããããã«ã¯ãéåžžãã¯ã©ã€ã¢ã³ãåŽã®ã«ã¹ã¿ã ããžãã¯ãšããã£ã³ã¯ç®¡çïŒäŸïŒãã£ã³ã¯ã®èå¥ãäžæçãªã¹ãã¬ãŒãžãæçµçãªã¢ã»ã³ããªïŒãåŠçããããã«èšèšããããµãŒããŒãšã³ããã€ã³ããå¿ èŠã§ãã
FastAPIã¯ã¯ã©ã€ã¢ã³ããéå§ãããã£ã³ã¯ãããã¢ããããŒããçµã¿èŸŒã¿ã§ãµããŒãããŠããŸãããããã®ããžãã¯ãFastAPIãšã³ããã€ã³ãå ã«å®è£ ã§ããŸããããã«ã¯ã次ã®ãšã³ããã€ã³ããäœæããããšãå«ãŸããŸãã
- åã ã®ãã¡ã€ã«ãã£ã³ã¯ãåä¿¡ããŸãã
- ãããã®ãã£ã³ã¯ãäžæçã«ä¿åãããããããããã®é åºãšãã£ã³ã¯ã®ç·æ°ã瀺ãã¡ã¿ããŒã¿ã䜿çšããŸãã
- ãã¹ãŠã®ãã£ã³ã¯ãã¢ããããŒãããããšãã«ã·ã°ãã«ãéä¿¡ããåã¢ã»ã³ããªããã»ã¹ãããªã¬ãŒãããšã³ããã€ã³ããŸãã¯ã¡ã«ããºã ãæäŸããŸãã
ããã¯ãããè€éãªåãçµã¿ã§ãããå€ãã®å Žåãã¯ã©ã€ã¢ã³ãåŽã®JavaScriptã©ã€ãã©ãªãå«ãŸããŸãã
åœéåãšã°ããŒããªãŒãŒã·ã§ã³ã«é¢ããèæ ®äºé
ã°ããŒãã«ãªãŒãã£ãšã³ã¹åãã®APIãæ§ç¯ããå Žåããã¡ã€ã«ã¢ããããŒãã«ã¯ç¹å¥ãªæ³šæãå¿ èŠã§ãã
- ãã¡ã€ã«åïŒäžçäžã®ãŠãŒã¶ãŒã¯ããã¡ã€ã«åã«ASCII以å€ã®æåïŒäŸïŒã¢ã¯ã»ã³ãã衚ææåïŒã䜿çšããå ŽåããããŸããã·ã¹ãã ããããã®ãã¡ã€ã«åãæ£ããåŠçããä¿åããããã«ããŠãã ãããUTF-8ãšã³ã³ãŒãã£ã³ã°ãäžè¬çã«æšæºã§ãããæ·±ãäºææ§ã«ã¯ãæ éãªãšã³ã³ãŒãã£ã³ã°/ãã³ãŒãã£ã³ã°ãšãµãã¿ã€ãºãå¿ èŠã«ãªãå ŽåããããŸãã
- ãã¡ã€ã«ãµã€ãºã®åäœïŒMBãšGBã¯äžè¬çã§ããããŠãŒã¶ãŒããã¡ã€ã«ãµã€ãºãã©ã®ããã«èªèããŠãããã«æ³šæããŠãã ãããå¶éããŠãŒã¶ãŒãã¬ã³ããªãŒãªæ¹æ³ã§è¡šç€ºããããšãéèŠã§ãã
- ã³ã³ãã³ãã¿ã€ãïŒãŠãŒã¶ãŒã¯ãããŸãäžè¬çã§ã¯ãªãMIMEã¿ã€ãã®ãã¡ã€ã«ãã¢ããããŒãããå ŽåããããŸããèš±å¯ãããã¿ã€ãã®ãªã¹ããå æ¬çã§ãããããŠãŒã¹ã±ãŒã¹ã«åãããŠååã«æè»ã§ããããšã確èªããŠãã ããã
- å°åã®èŠå¶ïŒããŸããŸãªåœã«ãããããŒã¿ã¬ãžãã³ã·ãŒæ³ããã³èŠå¶ã«æ³šæããŠãã ãããã¢ããããŒãããããã¡ã€ã«ã®ä¿åã«ã¯ããããã®ã«ãŒã«ãžã®æºæ ãå¿ èŠã«ãªãå ŽåããããŸãã
- ãŠãŒã¶ãŒã€ã³ã¿ãŒãã§ãŒã¹ïŒãã¡ã€ã«ãã¢ããããŒãããããã®ã¯ã©ã€ã¢ã³ãåŽã®ã€ã³ã¿ãŒãã§ãŒã¹ã¯ãçŽæçã§ããããŠãŒã¶ãŒã®èšèªãšãã±ãŒã«ããµããŒãããå¿ èŠããããŸãã
ãã¹ãçšã®ããŒã«ãšã©ã€ãã©ãª
ãã¡ã€ã«ã¢ããããŒããšã³ããã€ã³ãã®ãã¹ãã¯äžå¯æ¬ ã§ãã以äžã«ãäžè¬çãªããŒã«ã瀺ããŸãã
- Swagger UIïŒã€ã³ã¿ã©ã¯ãã£ãAPIããã¥ã¡ã³ãïŒïŒFastAPIã¯èªåçã«Swagger UIããã¥ã¡ã³ããçæããŸãããã©ãŠã¶ã€ã³ã¿ãŒãã§ãŒã¹ãããã¡ã€ã«ã¢ããããŒããçŽæ¥ãã¹ãã§ããŸãããã¡ã€ã«å ¥åãã£ãŒã«ããæ¢ããããã¡ã€ã«ã®éžæããã¿ã³ãã¯ãªãã¯ããŸãã
- PostmanïŒäžè¬çãªAPIéçºããã³ãã¹ãããŒã«ããã¡ã€ã«ã¢ããããŒããªã¯ãšã¹ããéä¿¡ããã«ã¯ïŒ
- ãªã¯ãšã¹ãã¡ãœãããPOSTã«èšå®ããŸãã
- APIãšã³ããã€ã³ãURLãå ¥åããŸãã
- ãBodyãã¿ãã«ç§»åããŸãã
- ã¿ã€ããšããŠãform-dataããéžæããŸãã
- ããŒãšå€ã®ãã¢ã§ããã¡ã€ã«ãã©ã¡ãŒã¿ã®ååïŒäŸïŒ
fileïŒãå ¥åããŸãã - ã¿ã€ãããããã¹ããããããã¡ã€ã«ãã«å€æŽããŸãã
- ããã¡ã€ã«ã®éžæããã¯ãªãã¯ããŠãããŒã«ã«ã·ã¹ãã ãããã¡ã€ã«ãéžæããŸãã
- ä»ã®ãã©ãŒã ãã£ãŒã«ããããå Žåã¯ãåæ§ã«è¿œå ããã¿ã€ãããããã¹ããã®ãŸãŸã«ããŸãã
- ãªã¯ãšã¹ããéä¿¡ããŸãã
- cURLïŒHTTPãªã¯ãšã¹ããè¡ãããã®ã³ãã³ãã©ã€ã³ããŒã«ã
- åäžã®ãã¡ã€ã«ã®å ŽåïŒ
curl -X POST -F "file=@/path/to/your/local/file.txt" http://localhost:8000/files/ - è€æ°ã®ãã¡ã€ã«ã®å ŽåïŒ
curl -X POST -F "files=@/path/to/file1.txt" -F "files=@/path/to/file2.png" http://localhost:8000/files/multiple/ - æ··åããŒã¿ã®å ŽåïŒ
curl -X POST -F "description=My description" -F "files=@/path/to/file.txt" http://localhost:8000/files/mixed/ - Pythonã®
requestsã©ã€ãã©ãªïŒããã°ã©ã ã«ãããã¹ãçšã
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())
çµè«
FastAPIã¯ãmultipartãã¡ã€ã«ã¢ããããŒããåŠçããããã®åŒ·åã§å¹ççãã€çŽæçãªæ¹æ³ãæäŸããŸããUploadFileã¿ã€ããšéåæããã°ã©ãã³ã°ã掻çšããããšã§ãéçºè
ã¯ãã¡ã€ã«åŠçæ©èœãã·ãŒã ã¬ã¹ã«çµ±åããå
ç¢ãªAPIãæ§ç¯ã§ããŸããã»ãã¥ãªãã£ãåªå
ããé©åãªãšã©ãŒåŠçãå®è£
ãããã¡ã€ã«ãšã³ã³ãŒãã£ã³ã°ãèŠå¶éµå®ãªã©ã®åŽé¢ã«å¯ŸåŠããŠãã°ããŒãã«ãŠãŒã¶ãŒããŒã¹ã®ããŒãºãèæ
®ããŠãã ããã
ã·ã³ãã«ãªç»åå ±æãµãŒãã¹ãæ§ç¯ããŠããå Žåã§ããè€éãªããã¥ã¡ã³ãåŠçãã©ãããã©ãŒã ãæ§ç¯ããŠããå Žåã§ããFastAPIã®ãã¡ã€ã«ã¢ããããŒãæ©èœããã¹ã¿ãŒããããšã¯éèŠãªè³ç£ãšãªããŸãããã®æ©èœãç¶ç¶çã«èª¿æ»ãããã¹ããã©ã¯ãã£ã¹ãå®è£ ããåœéçãªãªãŒãã£ãšã³ã¹ã®ããã«åªãããŠãŒã¶ãŒãšã¯ã¹ããªãšã³ã¹ãæäŸããŠãã ããã