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
本篇文章如有帮助到您,请给「翎野君」点个赞,感谢您的支持。