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

相关推荐
言之。13 小时前
Django REST Framework 中 @action 装饰器详解
数据库·sqlite
kida_yuan14 小时前
【从零开始】12. 一切回归原点
python·架构·nlp
欢脱的小猴子14 小时前
VUE3加载cesium,导入czml的星座后页面卡死BUG 修复
前端·vue.js·bug
高级测试工程师欧阳14 小时前
CSS 基础概念
前端·css·css3
前端小巷子14 小时前
JS 实现图片瀑布流布局
前端·javascript·面试
Juchecar14 小时前
AI教你常识之 npm / pnpm / package.json
前端
练习两年半的工程师14 小时前
AWS TechFest 2025: 智能体企业级开发流程、Strands Agents
人工智能·云计算·aws
月阳羊14 小时前
【硬件-笔试面试题-76】硬件/电子工程师,笔试面试题(知识点:H桥驱动电路的设计要点)
java·单片机·嵌入式硬件·面试·职场和发展
薛定谔的猫214 小时前
前端工程化系列(一):编码规范相关
前端·代码规范·前端工程化
ZKshun14 小时前
[ 前端性能优化 - 图片压缩 ] WebP格式的的图片性能到底有多优秀?
前端