[Flask] 플라스크와 AWS S3 연동하고 이미지 업로드하기 (boto3)
AWS S3는 Simple Storage Service의 준말입니다.
파일, 이미지, 동영상 등 다양한 유형의 데이터를 저장할 수 있습니다.
Flask와 S3를 연동하는 방법은 2가지가 있습니다.
✔ Flask의 확장 프로그램 Flask-S3 사용
✔ AWS에서 제공하는 Python SDK인 Boto3 사용
Flask-S3는 간단하게 활용할 수 있고, Boto3는 더 많은 유연성을 제공하지만
조금 더 복잡한 설정과 사용법을 가지고 있다고 합니다.
각각의 장단점이 있지만 저는 AWS에서 제공하는
Boto3를 사용해서 이미지를 업로드하는 API를 만들어 보겠습니다. 😀
📝S3 연동 및 파일 업로드 구현
⦁ 라이브러리 설치
pip install boto3
#파일이름 보안 라이브러리 - 해킹공격 방지
pip install werkzeug
✔ 필요한 라이브러리를 설치합니다. 가상 환경에 설치하는 것을 권장드립니다.
⦁ S3 연결
import boto3
from botocore.client import Config
s3 = boto3.client(
's3',
aws_access_key_id=app.config['S3_ACCESS_KEY'],
aws_secret_access_key=app.config['S3_SECRET_KEY'],
config=Config(signature_version='s3v4')
)
✔ 엑세스 키와 시크릿 키를 입력해야 합니다. app.config[]를 거치지 않고, 직접 입력해도 되지만
키가 노출되면 안되기 때문에 환경 변수를 사용하는 것을 추천드립니다.
👍 참고하세용 https://hyunki99.tistory.com/98
⦁ S3 파일 업로드
# request도 import가 필요합니다 !
from flask import Flask, request
#파일이름 보안 라이브러리
from werkzeug.utils import secure_filename
#이미지 업로드
@app.route('/imgupload', methods=['POST'])
def upload_file():
file = request.files['file']
if file:
filename = secure_filename(file.filename)
s3.upload_fileobj(file, app.config['S3_BUCKET_NAME'], filename)
return 'File uploaded successfully', 200
return 'No file selected', 404
✔ 파일 데이터는 FormData로 전송받아야 합니다. 요청에 file이라는 key로 전송된 파일을 가져옵니다. if문을 통해 파일이 정상적으로 전송되었는지 확인합니다.
✔ secure_filename()는 werkzeug에서 제공하는 함수입니다.
업로드된 파일의 이름을 보안 상 안전한 파일 이름으로 변환합니다. 악의적인 파일 이름으로 인한 보안 문제를 방지할 수 있습니다.
✔ 이후 성공적으로 완료되었다면 메세지와 함께 200 상태 코드를 반환합니다.
📝테스트 하기
⦁ test.html
<form enctype="multipart/form-data" method="POST" action="http://127.0.0.1:5000/imgupload">
<input type="file" name="file">
<input type="submit" value="Submit">
</form>
✔ 새로운 html파일을 하나 생성합니다. action에 들어가는 url은 서버 주소에 맞게 변경해야 합니다.
✔ 성공적으로 S3에 이미지가 업로드된 모습을 확인할 수 있습니다. 😎
📝에러 해결 과정
❗ 권한 에러 발생
botocore.exceptions.ClientError: An error occurred (AccessDenied) when calling the PutObject operation: Access Denied
내용에서 바로 알 수 있듯이, S3 버킷에 PutObject 권한이 없어서 발생한 에러입니다.
저는 버킷을 생성할 때 아무런 권한도 부여하지 않았기 때문에 해당 에러가 발생했습니다.
⦁ 버킷 권한 부여
✔ 버킷 → 권한 → 버킷 정책 탭에 접속합니다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::your-bucket-name/*"
}
]
}
✔ 버킷 정책에 다음과 같은 json을 추가해주면 에러가 해결되고 파일을 업로드할 수 있게 됩니다.
✔ 권한과 정책에 대한 더 자세한 정보는 공식문서를 참고하세요 !
https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-policy-language-overview.html
📝파일이 덮어쓰기 되는 문제 해결
S3에 파일을 저장할 때, 파일 이름이 동일하다면 기존 파일을 덮어쓰는 문제가 발생합니다.
따라서 이름을 고유하게 만드는 방법 중 하나인 UUID을 사용했습니다.
🤔 UUID 란?
UUID(Universally Unique IDentifier)는 네트워크상에서 고유성을 보장하는 ID를 만들기 위한 표준 규약이다. UUID는 다음과 같이 32개의 16진수로 구성되며 5개의 그룹으로 표시되고 각 그룹은 붙임표(-)로 구분한다.
280a8a4d-a27f-4d01-b031-2a003cc4c039 적어도 서기 3400년까지는 같은 UUID가 생성될 수 없다고 한다.
이러한 이유로 UUID를 데이터베이스의 프라이머리 키(primary key)로 종종 사용한다.
🤔 수정한 코드
#이미지 업로드
@app.route('/imgupload', methods=['POST'])
def upload_file():
file = request.files['file']
if file:
folder = 'img/' # 업로드할 폴더 이름
filename = secure_filename(file.filename)
unique_filename = str(uuid.uuid4()) + os.path.splitext(filename)[1] # UUID를 사용하여 파일 이름 고유성 보장
key = os.path.join(folder, unique_filename) # 경로 생성
s3.upload_fileobj(file, app.config['S3_BUCKET_NAME'], key)
return 'File uploaded successfully', 200
return 'No file selected', 404
+ S3의 특정 폴더에 업로드하는 코드도 추가 되어었습니다.
😎 결과
✔ 똑같은 파일이지만 고유하게 식별되는 파일 이름으로 저장된 모습입니다 !
참고 문헌 : https://wikidocs.net/131351