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

相关推荐
twcc_come1 分钟前
渗透第二次作业
数据库·oracle
AllData公司负责人1 小时前
【亲测好用】云原生数据平台能力演示
数据库·云原生·开源
摇滚侠2 小时前
npm 设置了阿里云镜像,然后全局安装了 pnpm,pnpm 还需要设置阿里云镜像吗
前端·阿里云·npm
CaracalTiger3 小时前
什么是Clawdbot?Clawdbot下载、安装、配置教程(最新版Moltbot)
python·编辑器·aigc·idea·ai编程·intellij idea·agi
WJX_KOI7 小时前
Open Notebook 一个开源的结合AI的记笔记软件
python
程序员清洒8 小时前
Flutter for OpenHarmony:GridView — 网格布局实现
android·前端·学习·flutter·华为
VX:Fegn08958 小时前
计算机毕业设计|基于ssm + vue超市管理系统(源码+数据库+文档)
前端·数据库·vue.js·spring boot·后端·课程设计
chipsense8 小时前
电流传感器型号从数据库查询并排序输出到网页的方法
数据库·php·传感器·霍尔电流传感器
踢足球09298 小时前
寒假打卡:2026-01-28
数据库·oracle
麦聪聊数据8 小时前
智慧医疗数据互联互通:使用 QuickAPI 构建实时诊疗数据交换层
数据库·sql·安全