Post

FastAPI 공식문서 따라하기[20] - Request Files

https://fastapi.tiangolo.com/tutorial/request-files/ 공식문서 따라하는 글

☑️ Request Files

File이라는 것을 통해 파일업로드 요청을 받을 수 있다.

두 가지 방식이 있다.

pip install python-multipart를 통해 필요한 모듈을 받는다.

1
2
3
4
5
6
7
8
9
from fastapi import FastAPI, File, UploadFile

app = FastAPI()


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

  • fastapi에서 UploadFile, File을 임포트 받는다.
  • file: bytes = File()구문을 통해 파일을 받는다.
  • 이렇게 명시해주지 않는다면 쿼리파라미터로 인식히거나 RequestBody로 인식할 수 있다.
  • 이렇게 하면 파일은 폼데이터로 받아야한다고 한다.
  • 위의 구문처럼 bytes로 자료형을 명시해주면 FastAPI는 파일을 읽고 bytes형태의 파일을 서버에 전달한다.
  • 이 모든것은 메모리에 저장되므로 파일이 작을수록 더 잘 동작한다고 한다.
  • 하지만 UploadFile을 사용하면 더 큰 혜택을 받을 수 있다.
1
2
3
4
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
    return {"filename": file.filename}

이렇게 사용하면 bytes로 받는것보다 이점이 있다.

  • File()구문을 작성하지 않아도 된다.
  • “spooled” file이다.
    • 만약 파일의 크기가 메모리 사이즈 이상을 넘어가면 디스크에 저장한다고 한다.
  • 이미지, 비디오, 큰 바이너리 파일 등과 같은 파일에 어울린다.
  • 메타데이터 정보도 해당 구문을 통해 얻을 수 있다.

☑️ UploadFile

다음과 같은 속성을 갖는다.

  • filename : 문자열 형태의 업로드 파일이름이다.
  • content_type : 문자열 형태의 컨텐츠 타입을 말한다.(ex : image/jpeg)
  • file : 객체 그자체를 말하는 것같다. 출력 해보면 다음과 같이 정보가 나온다.

또한, 다음과 같은 메소드를 포함하고 있다.

  • write(data) : str,bytes 형태의 데이터를 쓴다.
  • read(size) : size만큼의 파일을 읽는다.
  • seek(offset) : offset만큼의 바이트위치로 이동한다.
  • close() : 파일을 닫는다.

이 메소드를 사용하려면 async 구문에서는 “await”를 사용해야한다.

async구문에서는 다음과 같이 작성해야하지만,

1
contents = await.myfile.read()

일반적인 함수(def)에서는 다음과 같이 작성할 수 있다.

1
contents = myfile.file.read()

☑️ Optional File Upload

None을 통해서 디폴트값을 설정할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from fastapi import FastAPI, File, UploadFile

app = FastAPI()


@app.post("/files/")
async def create_file(file: bytes | None = File(default=None)):
    if not file:
        return {"message": "No file sent"}
    else:
        return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile | None = None):
    if not file:
        return {"message": "No upload file sent"}
    else:
        return {"filename": file.filename}

☑️ UploadFile with Additional Metadata

추가적인 메타데이터를 넣을 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from fastapi import FastAPI, File, UploadFile

app = FastAPI()


@app.post("/files/")
async def create_file(file: bytes = File(default=None, description="file read by kms")):
    if not file:
        return{"message" : "no file sent"}
    else:
        return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile = File(default=None,description= "upload file read by kms")):
    if not file:
        return{"message": "no upload file sent"}
    else:
        return {"filename": file.file}

메타데이터가 추가된 것을 확인할 수 있다.

☑️ Mutiple File Uploads

같은 시간에 파일을 여러개 요청 받을 수 있다.

대신 동일한 form field에서 form data를 받아야한다.

bytes방식, UploadFile방식 둘 다 받을 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse

app = FastAPI()


@app.post("/files")
async def create_files(files: list[bytes] = File(default=None)):
    return {"file_sizes": [len(file) for file in files]}

@app.post("/uploadfiles/")
async def create_upload_files(files: list[UploadFile]):
    return {"filenames " : [file.filename for file in files]}

@app.get("/")
async def main():
    content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>
    """
    return HTMLResponse(content=content)

http://localhost:8000/

에 들어가면 다음과 같이 폼 양식이 뜬다.

저기서 파일을 여러개 선택할 수 있다.

먼저 파일 사이즈를 받는 첫번째 요청에 대한 값을 확인해보았다.

다음은 여러 파일들의 이름을 반환하는 두 번째 요청를 테스트 해보았다.

☑️ Multiple File Uploads with Additional Metadata

파일을 여러개 받으면서 그에대한 메타정보도 추가로 작성할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse

app = FastAPI()


@app.post("/files/")
async def create_files(
    files: list[bytes] = File(description="Multiple files as bytes"),
):
    return {"file_sizes": [len(file) for file in files]}


@app.post("/uploadfiles/")
async def create_upload_files(
    files: list[UploadFile] = File(description="Multiple files as UploadFile"),
):
    return {"filenames": [file.filename for file in files]}

...

그냥 이런것이 있구나 하고 넘어가면 될 것 같다.

This post is licensed under CC BY 4.0 by the author.