요청 폼과 파일

요청 폼과 파일 (Request Forms and Files)

API를 만들다 보면 파일 업로드와 함께 폼 필드(예: 토큰 같은 것)를 한 요청 안에서 함께 받아야 할 때가 있어요. FastAPI는 이럴 때 FileForm을 함께 선언해서, 하나의 경로 동작 안에서 파일과 폼 데이터를 동시에 처리할 수 있게 해 줘요.

출처: 공식문서

먼저 python-multipart 설치하기

파일 업로드와 폼 데이터는 브라우저가 multipart/form-data 형식으로 보내요. 이 형식을 다루려면 먼저 python-multipart 패키지부터 설치해야 해요. 이게 없으면 파일을 받을 수 없으니, 어떤 내용인지는 나중에 천천히 봐도 좋아요. 일단 설치부터 하고 넘어갈게요.

$ uv add python-multipart

FileForm 가져오기

File, Form, 그리고 파일 객체를 담는 UploadFile을 가져와요.

from typing import Annotated

from fastapi import FastAPI, File, Form, UploadFile

app = FastAPI()

FileForm 파라미터 정의하기

BodyQuery를 선언하던 것과 똑같은 방식으로, 파일과 폼 파라미터를 선언하면 돼요. 파일은 File(), 폼 필드는 Form()으로 표시하죠.

from typing import Annotated

from fastapi import FastAPI, File, Form, UploadFile

app = FastAPI()


@app.post("/files/")
async def create_file(
    file: Annotated[bytes, File()],
    fileb: Annotated[UploadFile, File()],
    token: Annotated[str, Form()],
):
    return {
        "file_size": len(file),
        "token": token,
        "fileb_content_type": fileb.content_type,
    }

Annotated를 쓰고 싶지 않다면, 기본값으로 File()/Form()을 주는 방식도 동일하게 동작해요.

from fastapi import FastAPI, File, Form, UploadFile

app = FastAPI()


@app.post("/files/")
async def create_file(
    file: bytes = File(), fileb: UploadFile = File(), token: str = Form()
):
    return {
        "file_size": len(file),
        "token": token,
        "fileb_content_type": fileb.content_type,
    }

파일과 폼 필드가 폼 데이터로 업로드되면, 그 값들이 각각의 파라미터로 들어와요. 이때 주목할 점은 일부 파일은 bytes로, 일부는 UploadFile로 선언할 수 있다는 거예요. bytes로 받으면 파일 내용이 메모리에 통째로 올라오고, UploadFile로 받으면 파일 객체가 전달돼서 content_type 같은 메타데이터에도 접근할 수 있어요.

경고

하나의 경로 동작 안에 여러 개의 File/Form 파라미터를 선언할 수는 있어요. 하지만 JSON으로 받고 싶은 Body 필드는 같이 선언할 수 없어요. 요청 본문은 application/json이 아니라 multipart/form-data로 인코딩되기 때문이죠. 폼(혹은 파일)을 받는 동작에서는 본문도 폼 형식으로 오는 걸 기억해 두세요.

더 알아보기 (Learn more)