AWS S3 Lambda Python脚本函数实现图片自动转换为webp并上传至s3

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

相关推荐
若兰幽竹1 小时前
【从零开始编写数据库系统:架构设计与实现】第5章:查询执行引擎与火山模型
数据库·架构·数据库内核·toydb
keep one's resolveY1 小时前
SpringBoot实现重试机制的四种方案
java·spring boot·后端
我的xiaodoujiao2 小时前
API 接口自动化测试详细图文教程学习系列15--项目实战演练2
python·学习·测试工具·pytest
天空属于哈夫克32 小时前
企业微信API常见的错误和解决方案
java·数据库·企业微信
wenzhangli72 小时前
Ooder A2UI 核心架构深度解析:WEB 拦截层的设计与实现
前端·架构
东风破1372 小时前
DM8达梦数据库备份、恢复原理介绍
数据库·oracle·dm达梦数据库
鹏子训2 小时前
AI记忆新思路:用SQLite替代向量数据库,去EMBEDDINGS化,谷歌开源Google Always On Memory Agent
数据库·人工智能·sqlite·embedding
前端百草阁3 小时前
【前端性能优化全链路指南】从开发编写到构建运行的多维度实践
前端·性能优化
Frank_refuel3 小时前
终端环境下:Ubuntu 22.04.1 安装 MySQL 数据库
数据库·mysql·ubuntu
摇滚侠3 小时前
VMvare 虚拟机 Oracle19c 安装步骤,远程连接 Oracle19c,百度网盘安装包
java·oracle