본문 바로가기

웹 프레임워크/FastAPI

FastAPI - 11 (파일 요청 / 폼과 파일 요청)

출처: https://fastapi.tiangolo.com/tutorial/request-files/
출처: https://fastapi.tiangolo.com/tutorial/request-forms-and-files/
아래의 내용은 공식 사이트의 내용을 제 경험과 생각을 추가하여 다시 정리한 것 입니다.

File, UploadFile

fastapi는 파일 요청을 처리 하기 위해 FileUploadFile을 제공합니다.

클라이언트에서 업로드 한 파일을 받기 위해서는 python-multipart가 필요합니다. pip install python-multipart로 설치 할 수 있습니다.

# File과 UploadFile import
from fastapi import FastAPI, File, UploadFile

app = FastAPI()


# file은 기본값이 없으므로 필수 매개변수입니다.
# file은 클라이언트로 부터 업로드 된 파일을 나타냅니다.
# file 매개변수는 File() 유형이며 파일의 내용은 바이트(bytes) 형태로 제공됩니다.
@app.post("/files/")
async def create_file(file: bytes = File()):
    return {"file_size": len(file)}


# file은 기본값이 없으므로 필수 매개변수입니다.
# file은 클라이언트로 부터 업로드 된 파일을 나타냅니다.
# file 매개변수는 UploadFile 유형이며 파일의 내용은 바이트(bytes) 형태로 제공됩니다.
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
    return {"filename": file.filename}

테스트를 위해 HTML 화면을 만드는 코드를 추가 합니다.

# 기존에서 추가된 import
from fastapi import FastAPI, File, UploadFile, Form
from fastapi.responses import HTMLResponse

app = FastAPI()


@app.post("/files/")
async def create_file(file: bytes = File()):
    return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
    return {"filename": file.filename}
### 기존 코드 ###    


### 테스트를 위해 HTML 화면을 만드는 코드를 추가 ###
@app.get("/", response_class=HTMLResponse)
async def get_upload_form():
    return """
    <html>
        <head>
            <title>Upload File</title>
        </head>
        <body>
            <h1>Upload File</h1>
            <form action="/files/" method="post" enctype="multipart/form-data">
                <label for="file">Upload File (bytes):</label>
                <input type="file" name="file" id="file">
                <input type="submit" value="Upload">
            </form>
            <form action="/uploadfile/" method="post" enctype="multipart/form-data">
                <label for="uploadfile">Upload File (UploadFile):</label>
                <input type="file" name="file" id="uploadfile">
                <input type="submit" value="Upload">
            </form>
        </body>
    </html>
    """

테스트

http://localhost:8000/

위의 URL로 접속하면 아래와 같은 화면이 나옵니다.

업로드 할 파일을 선택합니다.

  • 먼저 bytes 형태로 업로드 된 파일을 처리하는 /files/를 테스트 합니다.
    Upload 버튼을 클릭하면 업로드 된 file의 size를 확인 할 수 있습니다.

  • 두번째로 UploadFile 형태로 업로드 된 파일을 처리하는 /uploadfile/를 테스트 합니다.
    Upload 버튼을 클릭하면 업로드 된 file의 이름을 확인 할 수 있습니다.

File, UploadFile 차이점

  1. File
    • 파일의 내용을 바이트(bytes) 형태로 직접 받습니다.
    • 업로드된 파일은 메모리에 저장됩니다. 이 방식은 작은 파일을 다룰 때 적합합니다.
    • File을 사용하는 경우, 파일에 대한 추가적인 정보(예: 파일명, MIME 타입 등)에 접근하기 어렵습니다.
  2. UploadFile
    • UploadFile은 비동기 방식으로 파일을 읽고 쓸 수 있는 메서드를 제공합니다. 이를 통해 FastAPI의 비동기 프로그래밍 모델과 잘 호환됩니다.
    • 대용량 파일을 처리할 때, UploadFile은 파일을 메모리에 전부 저장하지 않고, 디스크에 임시 파일을 생성합니다. 이는 메모리 사용을 최소화하고, 큰 파일도 효율적으로 처리할 수 있게 해줍니다.
    • UploadFile은 업로드된 파일과 관련된 추가 정보를 제공합니다. 예를 들어, file.filename은 업로드된 파일의 원래 이름을, file.content_type은 MIME 타입을 나타냅니다.
    • 주요 메서드
      • await file.read(size: int): 파일에서 지정된 크기만큼의 데이터를 비동기적으로 읽습니다.
      • await file.write(data: bytes): 파일에 데이터를 비동기적으로 씁니다 (일반적으로는 사용되지 않음).
      • await file.seek(offset: int): 파일의 지정된 위치로 포인터를 이동합니다.
      • await file.close(): 파일을 닫습니다. 파일 작업을 마친 후에 호출하는 것이 좋습니다.

폼 데이터와 함께 파일 업로드

FileForm을 사용하여 폼 데이터와 함께 파일을 업로드 할 수 있습니다.

File(File, UploadFkoe)과 폼 데이터

# File과 UploadFile, Form import
from fastapi import FastAPI, File, Form, UploadFile

app = FastAPI()


@app.post("/files/")
async def create_file(
    # file은 bytes 형태로 업로드 된 파일
    # fileb는 UploadFile 형태로 업로드 된 파일
    # token은 폼 데이터
    file: bytes = File(), fileb: UploadFile = File(), token: str = Form()
):
    return {
        "file_size": len(file),
        "token": token,
        "fileb_content_type": fileb.content_type,
    }

테스트를 위해 HTML 화면을 만드는 코드를 추가 합니다.

# 기존에서 추가된 import
from fastapi import FastAPI, File, Form, UploadFile, Response
from fastapi.responses import HTMLResponse

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,
    }
### 기존 코드 ###    


### 테스트를 위해 HTML 화면을 만드는 코드를 추가 ###
@app.get("/", response_class=HTMLResponse)
async def get_upload_form():
    return """
    <html>
        <head>
            <title>Upload File</title>
        </head>
        <body>
            <h1>Upload File</h1>
            <form action="/files/" method="post" enctype="multipart/form-data">
                <input type="file" name="file">
                <input type="file" name="fileb">
                <input type="text" name="token" placeholder="Token">
                <input type="submit" value="Upload">
            </form>
        </body>
    </html>
    """

테스트

http://localhost:8000/

위의 URL로 접속하면 아래와 같은 화면이 나옵니다.

업로드 할 파일 2개을 선택하고 폼 데이터(token)을 입력합니다.

Upload 버튼을 클릭하면 업로드 된 bytes file의 size와 UploadFile의 content_type, 그리고 폼 데이터(token)을 확인 할 수 있습니다.