使用亚马逊(AWS)云服务在S3上实现图片缩放功能(CloudFront/S3[AccessPoint/LambdaAccessPoint])

news2024/11/24 20:34:23

亚马逊云服务中的S3对象存储功能和国内阿里云的oss对象存储使用基本一致。但是涉及到存储内容处理时,两家有些差别。

比如:对于云存储中的图片资源,阿里云比较人性化对于基本的缩放裁剪功能已经帮我们封装好了,只需要在url地址后面拼接参数即可,但是亚马逊S3存储本身并不具备这个功能,但是亚马逊提供了很多种方式,虽然灵活但是使用门槛较高(尤其是亚马逊文档基本都是英文的🤣)。

下面详细说一下如何在亚马逊服务上基于S3存储实现图片处理(扩展开可以处理任意类型存储资源)

篇幅较长,看完能懂个大概

使用到的亚马逊服务:

Lambda函数服务:云服务商目前都在推的serverless一种实现,在云端特定功能上运行写好的代码,支持很多种语言,弹性扩展灵活方便。参考(首先要学会简单的创建lambda函数,后面需要使用函数处理图片)

S3对象存储:

        Access Point:作为S3存储的一个外部访问入口,一个access point对应一个S3bucket,一个S3 bucket可以对应多个access point. 常用于外部访问S3存储数据。

        Object Lambda Access Point:建立在Access Point之上的Lambda函数切点,可以对访问请求或者响应做进一步处理,或者修改。

CloudFront云端内容分发服务(cdn):亚马逊云端内容分发服务,实现对内容的实时定制化处理,可扩展性高,延迟低。

S3 + CloudFront + Lambda 实现图片缩放具体步骤

1. 在lambda控制台新建Lambda函数 参考:geting-started 本文所需代码如下:

注:代码来自亚马逊文档中的示例,稍作修改实现图片缩放功能

import boto3
import json
import os
import logging
from io import BytesIO
from PIL import Image, ImageDraw, ImageFont
from urllib import request
from urllib.parse import urlparse, parse_qs, unquote
from urllib.error import HTTPError
from typing import Optional

logger = logging.getLogger('S3-img-processing')
logger.addHandler(logging.StreamHandler())
logger.setLevel(getattr(logging, os.getenv('LOG_LEVEL', 'INFO')))
FILE_EXT = {
    'JPEG': ['.jpg', '.jpeg'],
    'PNG': ['.png'],
    'TIFF': ['.tif']
}
OPACITY = 64  # 0 = transparent and 255 = full solid


def get_img_encoding(file_ext: str) -> Optional[str]:
    result = None
    for key, value in FILE_EXT.items():
        if file_ext in value:
            result = key
            break
    return result

# 添加水印示例
def add_watermark(img: Image, text: str) -> Image:
    # font = ImageFont.truetype("AmazonEmber_Rg.ttf", 82)
    txt = Image.new('RGBA', img.size, (255, 255, 255, 0))
    if img.mode != 'RGBA':
        image = img.convert('RGBA')
    else:
        image = img

    d = ImageDraw.Draw(txt)
    # Positioning Text
    width, height = image.size
    text_width, text_height = d.textsize(text, font)
    x = width / 2 - text_width / 2
    y = height / 2 - text_height / 2
    # Applying Text
    d.text((x, y), text, fill=(255, 255, 255, OPACITY), font=font)
    # Combining Original Image with Text and Saving
    watermarked = Image.alpha_composite(image, txt)
    return watermarked

# 图片缩放
def resize_image(img, max_side_length=768):
    # 打开图像文件
    width, height = img.size
    print("原:宽X高", width, "x", height)
    # 计算缩放后的尺寸
    scale_factor = max_side_length / max(width, height)
    new_width = int(width * scale_factor)
    new_height = int(height * scale_factor)

    # 缩放图像
    resized_img = img.resize((new_width, new_height))
    return resized_img

def handler(event, context) -> dict:
    logger.debug(json.dumps(event))
    object_context = event["getObjectContext"]
    # Get the presigned URL to fetch the requested original object
    # from S3
    s3_url = object_context["inputS3Url"]
    # Extract the route and request token from the input context
    request_route = object_context["outputRoute"]
    request_token = object_context["outputToken"]
    parsed_url = urlparse(event['userRequest']['url'])
    object_key = parsed_url.path
    logger.info(f'Object to retrieve: {object_key}')
    parsed_qs = parse_qs(parsed_url.query)
    for k, v in parsed_qs.items():
        parsed_qs[k][0] = unquote(v[0])

    filename = os.path.splitext(os.path.basename(object_key))
    # Get the original S3 object using the presigned URL
    logger.info(f'S3 url: {s3_url}, parsed_url: {parsed_url}')
    req = request.Request(s3_url)
    try:
        response = request.urlopen(req)
    except HTTPError as e:
        logger.info(f'Error downloading the object. Error code: {e.code}')
        logger.exception(e.read())
        return {'status_code': e.code}

    if encoding := get_img_encoding(filename[1].lower()):
        logger.info(f'Compatible Image format found! Processing image: {"".join(filename)}')
        img = Image.open(response)
        logger.debug(f'Image format: {img.format}')
        logger.debug(f'Image mode: {img.mode}')
        logger.debug(f'Image Width: {img.width}')
        logger.debug(f'Image Height: {img.height}')

        # img_result = add_watermark(img, parsed_qs.get('X-Amz-watermark', ['Watermark'])[0])
        img_result = img
        if parsed_qs.get('size', [''])[0] != '':
            img_result = resize_image(img, int(parsed_qs.get('size', ['500'])[0], base=10))
        img_bytes = BytesIO()

        if img.mode != 'RGBA':
            # Watermark added an Alpha channel that is not compatible with JPEG. We need to convert to RGB to save
            img_result = img_result.convert('RGB')
            img_result.save(img_bytes, format='JPEG')
        else:
            # Will use the original image format (PNG, GIF, TIFF, etc.)
            img_result.save(img_bytes, encoding)
        img_bytes.seek(0)
        transformed_object = img_bytes.read()

    else:
        logger.info(f'File format not compatible. Bypass file: {"".join(filename)}')
        transformed_object = response.read()

    # Write object back to S3 Object Lambda
    s3 = boto3.client('s3')
    # The WriteGetObjectResponse API sends the transformed data
    if os.getenv('AWS_EXECUTION_ENV'):
        s3.write_get_object_response(
            Body=transformed_object,
            RequestRoute=request_route,
            RequestToken=request_token)
    else:
        # Running in a local environment. Saving the file locally
        with open(f'myImage{filename[1]}', 'wb') as f:
            logger.debug(f'Writing file: myImage{filename[1]} to the local filesystem')
            f.write(transformed_object)

    # Exit the Lambda function: return the status code
    return {'status_code': 200}

创建完成如下:

2. 亚马逊S3控制台左侧 Access Point 菜单创建Access Point

3.   亚马逊S3控制台左侧 Object Lambda Access Point 菜单创建Object Lambda Access Point

 其他选项保持默认,点击创建Object Lambda Access Point即可

4. CloudFront控制台创建分发

其他先保持默认,点击创建分发。

 5. 上面分别创建了所需资源,下面需要为上面创建的资源和S3 bucket配置访问策略权限。

      配置Bucket访问策略 ,点击bucket name名称进入下图,最后点击Bucket policy右侧的edit编辑按钮

 修改如下配置,保存。 (下面的配置为允许aws的所有服务访问s3内的指定bucket内所有对象,并且AccessPoint的账号等于指定的账号)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "*",
            "Resource": [
                "arn:aws:s3:::你的bucket名称",
                "arn:aws:s3:::你的bucket名称/*"
            ],
            "Condition": {
                "StringEquals": {
                    "s3:DataAccessPointAccount": "当前登录的账号ID"
                }
            }
        }
    ]
}

 配置Access Point访问策略

配置内容如下:(下面配置为允许lambda access point访问点  访问  当前 access point内所有对象资源)

{
    "Version": "2012-10-17",
    "Id": "default",
    "Statement": [
        {
            "Sid": "s3objlambda",
            "Effect": "Allow",
            "Principal": {
                "Service": "cloudfront.amazonaws.com"
            },
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:你的区域:当前帐号ID:accesspoint/你的AccessPoint名子标识(我这里是vapp-aws-image)",
                "arn:aws:s3:你的区域:当前帐号ID:accesspoint/你的AccessPoint名子标识/object/*"
            ],
            "Condition": {
                "ForAnyValue:StringEquals": {
                    "aws:CalledVia": "s3-object-lambda.amazonaws.com"
                }
            }
        }
    ]
}

配置Object Lambda Access Point访问策略

 配置策略如下:(下面的配置为允许登陆账号下的内容分发  访问指定的object lambda access point)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "cloudfront.amazonaws.com"
            },
            "Action": "s3-object-lambda:Get*",
            "Resource": "arn:aws:s3-object-lambda:你的区域:登录账号ID:accesspoint/你的ObjectLambdaAccessPoint名字",
            "Condition": {
                "StringEquals": {
                    "aws:SourceArn": "arn:aws:cloudfront::登录账号ID:distribution/刚才创建的分发ID"
                }
            }
        }
    ]
}

6. 资源和策略配置完成后,还有最后一步就是自定义访问参数,用于缓存生成的内容,避免每次访问都需要调用lambda函数处理(或者缓存了不正确的内容,因为默认缓存策略不包含自定义参数),本文自定义参数为size,表示当前图片最大边边长尺寸,等比缩放,需要为自定义参数创建一个Cache policy

 

 创建成功后,找到刚才创建的CloudFront分发,配置缓存策略如下:

 保存后,等会(几分钟)生效。

7. 测试

CloudFront控制台,找到刚才创建的Distribution分发,找到分发域名,如下图:

 到bucket中上传一个图片,然后使用该域名访问测试如下:

如果写的lambda函数有问题,会报错,到lambda控制台查找日志根据报错信息调试即可。 

 上面截图中使用的为英文版控制台,原因是由于亚马逊很多文档都是英文,个别有中文的翻译也不好,对比英文文档使用时会出现对不上号的情况,所以推荐大家使用亚马逊对应文档操作时使用英文控制台。

最后 理一下这套服务整个流程

参考:amazon-s3-object-lambda-with-cloudfront

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/754281.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

window环境下安装Node并修改保存缓存的位置

0, 卸载Node 打开cmd命令行窗口 输入: npm cache clean --force然后在控制面版中卸载node 1,官网下载Node.js 点击官网下载 如一台电脑需要多个node环境 可使用nvm命令进行操作安装并且可以切换 2, 配置环境变量 安装成功之后&#x…

竹云参编 |《数据经纪从业人员评价规范》团体标准在2023全球数字经济大会发布

经国务院批准,由北京市人民政府、国家发展和改革委员会、工业和信息化部、商务部、国家互联网信息办公室、中国科学技术协会共同主办的2023全球数字经济大会在中国北京国家会议中心隆重召开。 深圳竹云科技股份有限公司作为主要编制单位,联合深圳数据交…

新大陆物联网云平台-物联网云平台推荐-免费好用的物联网平台

一、前言 作为多年的物联网开发者,使用过很多付费的物联网云平台,包括阿里云、华为云等,也使用过很多免费开源的物联网云平台,但就操作来说,我认为最便利的还是新大陆物联网云平台(NLECloud - 新大陆物联网…

【Elasticsearch】DSL查询文档

目录 1.DSL查询文档 1.1.DSL查询分类 1.2.全文检索查询 1.2.1.使用场景 1.2.2.基本语法 1.2.3.示例 1.2.4.总结 1.3.精准查询 1.3.1.term查询 1.3.2.range查询 1.3.3.总结 1.4.地理坐标查询 1.4.1.矩形范围查询 1.4.2.附近查询 1.5.复合查询 1.5.1.相关性算分 …

vagrant和vitrulBox创建虚拟机后使用xshell连接

1. 先在cmd使用vagrant ssh连接主机, 修改系统配置,允许密码登录 vi /etc/ssh/sshd_config PasswordAuthentication no 将这行的no改成yes 2. 重启ssh service sshd restart 3.打开ssh,输入主机ip 端口22 账号root 密码默认为 vagrant

解决 param image not exist 与 image format error(百度 AI)

前言 注意,此文的 AI,是指识别图文、人脸的 AI 功能,而不是文心一言那种对话形 AI。 最近在尝试使用百度 AI 功能,很有趣是不假了,但也有很多坑,特此记录一下。 正文 后文以使用 通用物体和场景识别 功能…

Linux之磁盘管理

说一下linux中磁盘分区问题 首先每一个分区都是独立的 ,基本上来说都是可以独立分配空间的 但是一般如下目录是自动放到根目录下面的 如果根分区用完了,/home下面的分区空间还能用吗,对系统有什么影响 文件类型 给linux虚拟机扩展分区 备注&…

简爱思维导图怎么画?几个超实用绘制步骤赶紧get

简爱思维导图怎么画?思维导图是一种有效的信息组织和表达工具,能够帮助我们更好地整理思路、提高学习效率。下面这篇文章就带大家了解一下简爱思维导图的绘制步骤,并分享4个超实用步骤,助你快速掌握。 在绘制思维导图之前&#xf…

Linux中常用的监控性能的命令(sar、mpstat,vmstat, iostat,)详解

Linux中常用的监控性能的命令有: sar:能查看CPU的平均信息,还能查看指定CPU的信息。与mpstat相比,sar能查看CPU历史信息 mpstat:能查看所有CPU的平均信息,还能查看指定CPU的信息。 与sar相比&#xff0c…

解密横幅banner图的制作秘籍:一文帮你解答所有疑问

Banner是网站首页的关键元素之一。访问者进入网站后,一般首先看到的是Banner图。Banner图会很大程度上影响访问者对网站的第一印象,以及网站对访问者的吸引力。 如果banner图设计得很好,访问者会有兴趣继续阅读。如果Banner图设计得不好&…

【数据结构导论】第 7 章:排序

目录 一、概述 (1)基本概念 (2)排序分类 (3)排序文件的物理表示 —— 数组表示 二、插入排序(通过比较插入实现排序) (1)直接插入排序 ① 过程 ② 算…

猜数字小游戏但多语言版本(C、Java、Golang、python、JavaScript)

文章目录 前言C语言版本语法风格应用领域C 语言实现猜数字小游戏 Java语言版本语法风格应用领域Java语言实现猜数字小游戏 GO语言版本语法风格应用领域GO语言实现猜数字小游戏 python版本语法风格应用领域python语言实现猜数字小游戏 JavaScript版本语法风格应用领域JavaScript…

【雕爷学编程】Arduino动手做(160)---HLK-V20离线语音模块3

37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的&#x…

Python中绘制正弦波形、余弦波形及其复合波形的应用举例

Python中绘制正弦波形、余弦波形及其复合波形的应用举例 使用python进行绘图是其重要功能之一,本文讲解使用python进行正弦余弦波及其复合波形的绘制方法。 一、绘制正弦波 程序 import numpy as np import matplotlib.pyplot as plt tnp.arange(-10,10,0.1) yn…

NavigationStack, Toolbar 的使用

1. NavigationStack 导航堆栈的使用,NavigationView 已过时 1.1 实现 /// 导航堆栈 (懒加载) iOS 16 版本 新特性 struct NavigationStackBootcamp: View {let fruits ["Apple", "Orange", "Banana"]//["one","two…

信贷系统开发设计基础(二)

目录 架构演进篇 01 信贷架构演进概述 02 单体架构案例简介 03 单体系统群架构案例分析 04 微服务案例分析 架构演进篇 01 信贷架构演进概述 02 单体架构案例简介 03 单体系统群架构案例分析 04 微服务案例分析 总结: ---------------------------------------…

MySQL数据库管理——用户管理(二)

文章目录 一.表结构(增加字段)1.创建表结构2.复制数据表2.1 复制格式2.2 克隆表 3.清空表,删除表内的所有数据4.创建临时表 二.MySQL中6种常见的约束1.外键的定义2.创建外键约束作用3.创建主表blue4.创建从表Icecream5.为主表blue添加一个主键约束。主键名建议以&qu…

复习第四课 C语言-分支语句和循环

目录 【1】字符输入输出 【2】C语言下的垃圾字符回收 【3】分支语句 【4】循环 练习: 【1】字符输入输出 按字符的输入输出 int getchar(void); 功能:从终端输入一个字符 参数:无 返回值:输入字符的ASCII值int putchar(int…

中信银行西安分行举办金融助力外贸企业“走出去“高端论坛

7月14日,中信银行西安分行联合中国出口信用保险公司陕西分公司、西安市工商联举办"智汇西安、信融全球"——金融助力外贸企业"走出去"高端论坛。该论坛紧跟“加快建设贸易强国”的战略指引,以创新金融服务助力外贸企业融入高水平对外…

ConfigMap/Secret:配置、定制我的应用

首先你要知道,应用程序有很多类别的配置信息,但从数据安全的角度来看可以分成两类: 一类是明文配置,也就是不保密,可以任意查询修改,比如服务端口、运行参数、文件路径等等。另一类则是机密配置&#xff0…