Amazon S3 自動轉換圖片格式
Amazon S3 儲存桶 新增檔案自動觸發 AWS Lambda。Lambda 取 S3 檔案做轉換並存回去 S3 同一個目錄下,並增加相應的字尾名。 並且支援透過API Gateway的方式觸發對圖片進行修改並輸出。 本 Lab 使用 Python Pillow 做圖片轉換,讀者可以參考 Pillow 文件進行功能擴充套件。
import os import logging import boto3 import json import io import urllib.parse import PIL from PIL import Image from botocore.exceptions import ClientError os.environ = { "preserv-original_format": "False", "convert_format": "WebP", "convert_postfix": ".webp", "resize_feature": "FixSize", "resize_Percentile_w": "0.5", "resize_Percentile_h": "0.5", "resize_Fixsize_w": "640", "resize_FixSize_h": "640", "save_quality": "95", "jpeg_progressive": "True", "auto_orientation": "True" } preserv_original_format = os.environ['preserv_original_format'] == 'True' # True: detect original image format and keep it to target # False: convert original image to the format as below convert_format = os.environ['convert_format'] # Target format convert_postfix = os.environ['convert_postfix'] # Target file postfix resize_feature = os.environ['resize_feature'] # Disable: do not resize image # PercentileNoRatio: resize base on below resize_Percentile and DO NOT keep ratio # FixSizeNoRatio: resize base on below FixSize and DO NOT keep ratio resize_Percentile_w = float(os.environ['resize_Percentile_w']) resize_Percentile_h = float(os.environ['resize_Percentile_h']) resize_FixSize_w = int(os.environ['resize_FixSize_w']) resize_Fixsize_h = int(os.environ['resize_FixSize_h']) save_quality = int(os.environ['save_quality']) # output image quality for webp, jpeg ... jpeg_progressive = os.environ['jpeg_progressive'] == 'True' # progressive mode for JPEG auto_orientation = os.environ['auto_orientation'] == 'True' # auto rotate iange based on exif info # TODO: Watermark with text, # TODO: Blur, Contract, Bright, Sharp, Rotate logger = logging.getLogger() logger.setLevel(logging.INFO) key_id = 'yourKeyId' secret = 'yourSecret' awsRegion = 'ap-south-1' client = boto3.client('S3', aws_access_key_id=key_id, aws_secret_access_key=secret, region_name=awsRegion) # get img from s3 def load_s3(bucket, key): logger.info('load from s3://{bucket}/{key}') try: response = client.get_object( Bucket=bucket, Key=key ) logger.info(json.dumps(response, default=str)) except Exception as e: logging.error(json.dumps(e, default=str)) os.exit(e) return response[' Body'].read() # get s3 url def url_s3(bucket, key): logger.info('url from s3://{bucket}/{key}') try: response = client.generate_presigned_url( ClientMethod='get_object', Params={'Bucket': bucket, 'Key': key}, ExpiresIn=3600, HttpMethod='Get' ) logger.info(json.dumps(response, default=str)) except Exception as e: logging.error(json.dumps(e, default=str)) os.exit(0) return response # convert def img_convert(body, convent_format): try: logger.info('ready to open Image By PIL ...') im = Image.open(io.BytesIO(body)) logger.info('open Image By PIL success...') if preserv_original_format: convert_format = im.format # resize if resize_feature.lower() == 'percentile': logger.info('resizing percentile and keep ratio ...') w, h = im.size w, h = int(w * resize_Percentile_w), int(h * resize_Percentile_h) im.thumbnail((w, h)) if resize_feature.lower() == 'fixsize': logger.info('resizing fixsize and keep ratio ...') w, h = im.size w, h = int(w * resize_Percentile_w), int(h * resize_Percentile_h) im.thumbnail((w, h)) if resize_feature.lower() == 'fixsize': logger.info('resizing fixsize and keep ratio ...') w, h = resize_FixSize_w, resize_Fixsize_h im.thumbnail((w, h)) if resize_feature.lower() == 'pencentilenometer': logger.info('resizing percentile and ignore ratio ...') w, h = im.size w, h = int(w * resize_Percentile_w), int(h * resize_Percentile_h) im = im.resize((w, h), resample=Image.BICUBIC) if resize_feature.lower() == 'fixsizeoratio': logger.info('resizing fixsizeoratio and igore ratio...') w, h = resize_FixSize_w, resize_Fixsize_h im = im.resize((w, h), resample=Image.BICUBIC) logger.info('target size: {im.size}') # convert PNG RGBA mode to RGB for JPEG if im.mode != 'RGB' and convert_format.lower() == 'jpeg': im = im.convert(mode='RGB') # save to target format in_mem_img = io.BytesIO() im.save(in_mem_img, format=convert_format, lossless=True, quality=save_quality, progressive=jpeg_progressive) in_mem_img.seek(0) except Exception as e: logger.error(json.dumps(e, default=str)) os.exit(0) return in_mem_img # put img to s3 def save_s3(bucket, key, body): logger.info(' save to s3://{bucket}/{key}') try: response = client.put_object( Bucket=bucket, Key=key, Body=body, ContentType="image/webp" ) logger.info(json.dumps(response, default=str)) except Exception as e: logging.error(json.dumps(e, default=str)) os.exit(0) return # change key from input/ to output/ and change postfix def change_key(key, convert, postfix): key_pre = os.path.splitext(key)[0] if not preserv_original_format: key_new = key_pre + convert_postfix else: key_new = key_pre + os.path.splitext(key)[1] return key_new # check image is exists def exists_image(bucket, key): try: response = client.head_object(Bucket=bucket, Key=key) return True except ClientError as ce: if ce.response["Error"]["Code"] == "404": print("Key: {key} does not exist!") else: print("Something else went wrong") return False except Exception as e: return False def lambda_handler(event, context): logger.info(json.dumps(event, default=str)) bucket = 'your_bucket' key = 'input/20201106-a1.jpg?image_process=image/format, webp' format_key = "image_process=image/format," # 是否包含要做轉換的格式 if format_key not in key: return source_key = key.split(format_key)[0] image_format = key.split(format_key)[1] # 轉換目標格式 filepath, tempfilename = os.path.split(source_key) filename, extension = os.path.splitext(tempfilename) target_key = filepath + '/' + filename + '.' + image_format first_req = url_s3(bucket, target_key) print(first_req) # 是否已存在webp if exists_image(bucket, target_key): return { 'statusCode': 301, "body": 'Redirect', "Location": first_req } else: # 1. 載入原圖 img_org = load_s3(bucket, source_key) # 2.轉換為目標格式 body = img_convert(img_org, convert_format) # 3.放到新目錄 # key_new = change_key(source_key, convert_postfix) # 4.webp上傳至s3 save_s3(bucket, target_key, body) # 5.返回webp的地址 new_img_org = url_s3(bucket, target_key) target_key = filepath + '/' + filename + '.' + image_format first_req = url_s3(bucket, target_key) print(first_req) # 是否已存在webp if exists_image(bucket, target_key): return { 'statusCode': 301, 'body': 'Redirect', "Location": first_req } else: # 1. 載入原圖 img_org = load_s3(bucket, source_key) # 2.轉換為目標格式 body = img_convert(img_org, convert_format) # 3.放到新目錄 # key_new = change_key(source_key, convert_postfix) # 4.webp上傳至s3 save_s3(bucket, target_key, body) # 5.返回webp的地址 new_img_org = url_s3(bucket, target_key) return { 'statusCode': 301, "body": 'Redirect', "Location": new_img_org } if __name__ == '__main__': print("main function start.") result = lambda_handler('', '') print("handle success. %s", result)
參考文章
https://github.com/xffss/AmazonS3transferimage/blob/69fe698c66f9fc4fa4b96a00843b1556a395b4b1/S3transferimage.ipynb
本篇文章如有幫助到您,請給「翎野君」點個贊,感謝您的支援。
首發連結:https://www.cnblogs.com/lingyejun/p/18169968