Backend/FastAPI

[FastAPI] AWS S3에 이미지 업로드하기

mopil 2023. 12. 3. 13:40
반응형

FastAPI에서 AWS S3에 이미지 업로드하는 방법을 공유한다

 

# S3 버킷 생성

버킷 생성 부분은 따로 다루지 않겠다. 구글에 검색하면 자료가 많이 나오니 참조하면 될 것 같다.

 

퍼블릭 엑세스를 모두 허용했는데도 access denied가 뜬다면, 권한 정책을 잘못 설정한 것일 가능성이 있으므로 다음을 체크해본다.

 

https://dev.classmethod.jp/articles/if-access-denied-appears-even-though-you-disabled-public-access-blocking-on-amazon-s3-what-is-the-workaround/

 

Amazon S3에서 퍼블릭 액세스 차단을 비활성화 했음에도 AccessDenied이 뜨는 경우 해결 방법은? | Develo

Amazon S3에서 퍼블릭 액세스 차단을 비활성화 했음에도 AccessDenied이 뜨는 경우 해결 방법에 대해서 정리해 봤습니다.

dev.classmethod.jp

 

 

# boto3

python에서 aws s3에 접속하기 위해서는 boto3라는 aws sdk를 사용한다.

 

pip install boto3

 

먼저 boto3를 인스톨해준다.

 

s3 = boto3.client(
    "s3", aws_access_key_id=AWS_S3_ACCESS_KEY, aws_secret_access_key=AWS_S3_PRIVATE_KEY
)

그런다음 s3 클라이언트를 생성해준다.

 

이상하게 boto3는 자동완성 기능을 지원하지 않는데, 일부러 동적으로 코드가 생성되게끔 만들어놔서 내부 구현을 못 보게(?) 해놓은 것 같다... 

 

자동완성이 되지 않아도 정상적으로 코드를 작성중인 것이니 안심하고 따라오면 된다.

 

# 파일 업로드

boto3에서는 파일 업로드에 관하여 다음 두 메서드를 제공한다.

 

upload_file(파일이름, 버킷이름, s3키값)

upload_fileobj(파일자체, 버킷이름, s3키값)

 

upload_file은 로컬에 있는 파일을 경로로 업로드하는 함수고, upload_fileobj는 바이트스트림으로 읽어서 바로 업로드하는 메서드이다.

 

https://stackoverflow.com/questions/52336902/what-is-the-difference-between-s3-client-upload-file-and-s3-client-upload-file

 

What is the difference between S3.Client.upload_file() and S3.Client.upload_fileobj()?

According to S3.Client.upload_file and S3.Client.upload_fileobj, upload_fileobj may sound faster. But does anyone know specifics? Should I just upload the file, or should I open the file in binary ...

stackoverflow.com

 

보다 구체적인 차이점은 공식문서나 스택오버플로우를 참조할 것 

 

마찬가지로 자동완성이 안 되어서 답답하지만 참고 진행한다.

 

 

여기서는 upload_fileobj를 사용하여 읽은 파일을 바로 업로드하는 코드를 작성해볼것이다.

 

# 완성 코드

@router.post("/upload")
async def upload(file: UploadFile, directory: str):
    if directory not in directories:
        raise HTTPException(status_code=400, detail="유효하지 않는 디렉토리에요")

    filename = f"{str(uuid.uuid4())}.jpg"
    s3_key = f"{directory}/{filename}"

    try:
        s3.upload_fileobj(file.file, BUCKET_NAME, s3_key)
    except (BotoCoreError, ClientError) as e:
        raise HTTPException(status_code=500, detail=f"S3 upload fails: {str(e)}")

    url = "https://s3-ap-northeast-2.amazonaws.com/%s/%s" % (
        BUCKET_NAME,
        urllib.parse.quote(s3_key, safe="~()*!.'"),
    )
    return JSONResponse(content={"url": url})

 

query param으로 디렉토리 (S3 폴더명)을 받고 해당 디렉토리 + 서버파일명을 생성하여 파일을 저장한다.

 

파일이름은 uuid로 생성하였고, s3_key는 s3에 저장되는 경로+파일명을 말한다.

ex. images/123asd342-asdfasdf2312-asdfasd.jpg

 

        s3.upload_fileobj(file.file, BUCKET_NAME, s3_key)

FastAPI UploadFile객체의 file 변수는 바이너리타입이라 이를 바로 전달해주면 된다.

 

 

    url = "https://s3-ap-northeast-2.amazonaws.com/%s/%s" % (
        BUCKET_NAME,
        urllib.parse.quote(s3_key, safe="~()*!.'"),
    )

리턴 값으로는 s3 파일에 접근할 수 있는 url을 리턴하는데, boto3에는 url을 가져올 수 있는 방법이 따로 없다(...)

 

어차피 s3 url은 정적이므로, 이를 직접 만들어서 리턴하도록 구현하였다.

https://stackoverflow.com/questions/48608570/python-3-boto-3-aws-s3-get-object-url

 

Python 3 Boto 3, AWS S3: Get object URL

I need to retrieve an public object URL directly after uploading a file, this to be able to store it in a database. This is my upload code: s3 = boto3.resource('s3') s3bucket.upload_file(fil...

stackoverflow.com

 

반응형