파이썬 프로젝트에서 이미지파일이 request로 들어오면 리사이즈(소, 중, 대)를 해서 s3에 업로드하는 함수를 만들어보자.
import uuid, io, os
from PIL import Image
from connection import get_s3_connection
[0] def resize_to_big(self, image_file):
[1] standard_size = 640
try:
[2] with Image.open(image_file) as opened_image:
[3] small = (int(standard_size), int(opened_image.size[1]*(standard_size/opened_image.size[0])))
resized_image_small = opened_image.resize(small)
[4] small_io = io.BytesIO()
[5] resized_image_small.save(small_io, "JPEG")
[6] small_io.seek(0)
[7] return [small_io, str(uuid.uuid4())]
except:
[8] return None
[0] 파라미터로 파일객체(이미지 파일로 유효성검사가 끝난)를 받는다.
[1] 가로의 길이 640을 고정 값을 잡는다.
[2] 파라미터로 들어온 파일객체를 pillow 라이브러리를 사용해 open해서 pillow의 Image클래스의 인스턴스로 만들어준다.(opened_image는 필로우 객체가 됨.)
[3] 가로의 길이를 기준으로 세로길이를 리사이징한다.
[4] 파일객체를 bytes형태로 메모리상에 일시적으로 저장하기 위해서 buffer를 만들어준다.
[5] pillow객체를 저장할 때 내 컴퓨터의 하드가 아닌 bytes io(즉, 메모리)에 jpeg파일 포멧으로 저장한다. 함수가 실행되는 scope동안 이 pillow객체는 bytes형태로 메모리상에 저장되게 된다.
[6] 메모리상에 bytes형태로 써지기 때문에 커서가 제일 뒤에 가있는 상태일 것이다. 따라서 커서를 제일 첫번째로 옮겨준다. s3에 업로드를 할때는 커서의 뒤부터 업로드가 되기 때문에 bytes로 써지고 커서가 제일 뒤로가있는 상태에서는 s3에 아무것도 업로드가 되지 않는다. seek(0)을 통해서 커서를 제일 처음으로 위치시키고 커서 뒤쪽을 전부 s3에 업로드해야 온전한 이미지파일이 업로드 된다.
[7] 메모리상의 bytes와 랜덤으로 생성된 uuid(이미지업로드 함수에서 파일이름으로 쓰일 예정)를 어레이 형태로 리턴한다.
[8] 만약 리사이즈에 실패하면 None을 리턴해서 리사이즈 함수를 호출하는 곳에서 None이 들어왔을 때 error처리를 하도록 만들어준다.
import boto3
[1] S3_CONFIG = {
'AWS_ACCESS_KEY_ID': 'KEY_ID',
'AWS_SECRET_ACCESS_KEY': 'SECRET_ACCESS_KEY',
'S3_BUCKET_NAME': 'BUCKET_NAME',
'REGION_NAME': 'REGION_NAME'
}
def get_s3_connection():
[2] s3_connection = boto3.client(
's3',
aws_access_key_id=S3_CONFIG['AWS_ACCESS_KEY_ID'],
aws_secret_access_key=S3_CONFIG['AWS_SECRET_ACCESS_KEY'],
region_name=S3_CONFIG['REGION_NAME'],
)
[3] return s3_connection
boto3 라이브러리를 활용하여 s3 connection을 만들어보자.
[1] 우선 s3 연결에 필요한 값들을 변수로 선언해준다. 이 값들은 s3의 iam유저와 버킷을 만들 때 받아오는 값들이다.
[2] boto3라이브러리의 client를 사용하여 위에서 정의한 s3 개인정보를 넣어준다.
[3] 리턴값으로 s3와 연결된 객체를 리턴한다. connection만 관리하는 파일을 모듈화 하여 s3 connection이 필요한 곳에서 import해서 사용하도록 한다.
import uuid, io, os
from PIL import Image
from connection import get_s3_connection
[1] image_file_1 = request.files.get('image_file_1', None)
[2] if image_file_1:
# 들어온 파일의 사이즈와 확장자를 구함.
[3] image_file_size = os.fstat(image_file_1.fileno()).st_size
[4] image_file_form = image_file_1.content_type
# 이미지 파일이 아닌 다른형식의 파일이 들어오는 것을 차단.
[5] if not ('image' in image_file_form):
return jsonify({'message': 'INVALID_FILE'}), 400
# 들어온 이미지 크기가 10MB보다 크면 request를 받지 않음.
[6] if image_file_size > 10485760:
return jsonify({'message': 'INVALID_IMAGE1'}), 400
# big_size 업로드
[7] big_size_buffer = self.resize_to_big(image_file_1)
[8] if not big_size_buffer:
return jsonify({"message": "INVALID_IMAGE"}), 400
# 이미지를 올리다가 에러가 날 경우를 잡아줌.
[9] try:
s3.put_object(
Body=big_size_buffer[0],
Bucket="brandi-intern",
Key=big_size_buffer[1],
[9-1] ContentType='image/jpeg'
)
except Exception as e:
print(f'error1 : {e}')
return jsonify({'message' : 'S3_UPLOAD_FAIL'}), 500
[10] big_size_url = f'https://brandi-intern.s3.ap-northeast-2.amazonaws.com/{big_size_buffer[1]}'
[11] data['image_file_1']['big_size_url'] = big_size_url
data['image_file_1']['big_image_size_id'] = 1
[1] 우선 request안에있는 files를 열어서 을 가져와 파일객체로 만들어준다. 파일이 들어오지 않은 경우 None을 변수에 담는다.
[2] 파일이 들어왔을 때 모든 로직을 실행한다. 파일이 들어오지 않았다면 아무일도 일어나지 않는다.
[3][6] 파이썬의 os 라이브러리를 통해서 파일객체의 크기를 구해주고 일정크기 이상의 파일이 들어오는 것을 막아준다.
[4][5] 파일의 포멧을 확인한다. 그리고 파일의 포멧에 'image'가 들어가지 않으면 error를 리턴한다.
[7] 위에서 정의한 이미지 리사이즈 함수에 유효성검사(파일포멧, 파일사이즈 확인)가 끝난 파일을 parameter로 넘겨준다.
[8] 값이 넘어오지않으면(리사이즈 함수에서 이미 try-catch로 리사이즈에 실패하면 none을 리턴하도록 설계된상태) error를 리턴한다.
[9] 이미지를 s3에 업로드하는 과정에서 발생하는 error를 try-catch로 잡아준다.
[9-1] content_type을 명시해주지 않으면 url을 클릭하면 파일을 다운로드하게 된다. 여기서는 url을 클릭해서 브라우저상에서 이미지를 load 할 수 있어야 하기 때문에 content_type을 넣어주도록 한다.
[10] 이미지가 올라간 s3주소뒤에 이미지파일의 이름(이름이 중복되는것을 막기위해 여기서는 uuid로 이름을 지정한다.)을 넣어준 url을 리턴한다.
[11] 이미지 url과 사이즈를 리턴할 dictionary에 넣어준다.