暂无图片
暂无图片
暂无图片
暂无图片
1
暂无图片

手把手教程 | 基于无服务器技术及 Amazon S3 对象存储的图片处理方案

西云数据云计算 2022-05-25
1128




Amazon S3 对象存储为用户提供了一种方便可靠的云端数据存储服务,有着广泛的应用场景。

对象数据种类繁多,包括图片、文档、日志等。

很多场景下,图片数据是提供给应用前端的用户来使用的,例如一个社交应用中用户头像或者照片的展示,或者一个在线设计网站所需要用到的素材。

这些应用有一个共同的需求——图片需要被处理。




一个社交应用,可能需要根据用户的网络情况和设备情况,加载不同分辨率的图片;或者对用户分享的图片进行亮度或者对比度调节,增强显示效果。


一个图片分享网站,可能需要对用户上传的图片进行再加工。比如将图片进行虚化处理,突出照片中的主体;或者需要将彩色图片变换成黑白图片,增加艺术效果;也可能需要对图片的质量、锐度进行调整;或者对图片进行裁切等操作,避免存储空间无限增长。


一些设计网站,基于版权原因,可能需要给图片添加水印,也可能需要对图片进行格式转换,以适应不同的工作流程。


因此,一个自然的想法就是,既然 Amazon S3 服务是一个理想的图片存放解决方案,那么可否再提供一系列与之配套的图片处理 API,使得用户可以直接对图片进行一些必要的处理,而不是需要每个用户自己编写重复的代码来实现这套逻辑?

为了解决这个问题,由西云数据运营的亚马逊云科技中国(宁夏)区域推出了图片处理 API 的解决方案,使得 Amazon S3 用户可以获得更好的使用体验和更高的产品附加值。

本文会详细介绍该方案的架构、部署、使用及成本细节,以期对用户在使用本方案时起到抛砖引玉的作用。






架构介绍







本方案提供两种实现方式,一种基于 Lambda Function,另一种基于 ECS Fargate。两种实现方式差异主要在于图片处理逻辑的实现载体不同,一个代码运行在 Lambda Function 上,另一个代码运行在 ECS Fargate 容器之上。

其中 Lambda 无法直接响应 HTTP 请求,因此需要 API Gateway 进行中转(Lambda Function URL 已在某些区域推出)。而运行在 ECS Fargate 之上的容器可以直接响应 HTTP 请求,因此无需 API Gateway,但是需要 ALB 进行负载均衡。基于 Lambda Function 的实现方式如图一所示。

▲图 1 基于 Lambda Function 的架构


▲图 2 基于 ECS Fargate 的架构


方案部署后,会产生以下系统资源:

  1. Cloudfront Distribution:为整个方案提供缓存服务,降低请求响应的延迟和整个方案的使用成本。

  2.  API Gateway:将没有命中缓存的 HTTP 请求转换为 Lambda Function 的事件,从而实现 Lambda Function 的调用。

  3. Lambda Function:根据请求的内容判断图片是否需要处理,无需处理的,直接从 S3 存储中加载图片返回给用户,需要处理的,调用相关代码进行用户要求的图片处理,然后将结果返回给用户。

  4. S3 Bucket:方案中涉及两个存储桶,一个用来存储图片处理的日志数据,另一个是用户指定的先前创建好的 bucket,用来存放原始图片数据。


该实现方式在特殊情况下会受限于 Lambda Function 本身所带来的限制,如处理后图片的大小必须小于 6MB,图片处理时间也有时长限制(Lambda Function 目前最长运行时间为 15 分钟),具体请复制下方链接至浏览器见 Lambda quotas。

https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html


基于 ECS Fargate 的实现架构图参见图 2,与图 1 的差别在于,所有图片处理的逻辑代码被封装成为一个 docker image,在部署过程中该 image 会被部署到 ECS Fargate 服务中。由于采用容器的方式运行,这种实现方式可以规避 Lambda Function 所带来的各种运行限制,缺点是部署方式目前相对麻烦,需要使用 CDK 来进行。

该方案部署后,会产生如下系统资源:
  1. Cloudfront Distribution:为整个方案提供缓存服务,降低请求响应的延迟和整个方案的使用成本。

  2. Application Load Balancer:将没有命中缓存的 HTTP 请求转发到后台的容器,并对 HTTP 请求进行负载均衡。

  3. ECS Fargate:相关的图片处理逻辑会被打包成 docker image,并在 ECS Fargate 上生成相应的容器,响应前端图片处理请求。

  4. S3 Bucket:方案中涉及两个存储桶,一个用来存储图片处理的日志数据,另一个是用户指定的先前创建好的 bucket,用来存放原始图片数据。







部署方式







方案的两种实现方式对应着两种部署方式。对于 ECS Fargate 的实现,我们采用了 CDK 的部署方式(Cloud Development Kit (Amazon CDK) )是一种开源软件开发框架,可让您使用熟悉的编程语言来定义云应用程序资源,请复制下方链接至浏览器详见 What is the Amazon CDK?。
https://docs.aws.amazon.com/cdk/v2/guide/home.html

详细部署步骤如下:


前置条件

  • 安装 Node.js,且 nodejs 版本 >= 12.0.0 

  • 安装 docker Get Docker | Docker Documentation

  • 安装 YARN Installation | Yarn - Package Manager

  • 安装 CDK,Getting startedwith the Amazon CDK

  • 执行 CDK Bootstrap,Bootstrapping


以上组件安装详细步骤读者可查看各组件官方文档。


 部署步骤
  • 获取方案代码(可联系我们获得具体代码库地址)

  • 进入上一步下载的文件夹后,执行 cd source/constructs

  • yarn

  • yarn test

  • 执行下述命令之一进行部署

   ■ CDK_DEPLOY_REGION=us-west-2 yarn deploy serverless-ecs-image-handler-stack (此命令会新建一个 VPC,请将红色的 us-west-2 替换成您的 region)。

   ■ CDK_DEPLOY_REGION=us-west-2 yarn deploy serverless-ecs-image-handler-stack -c use_vpc_id=xxxxx(此命令会在一个已有的 VPC 中进行部署,请将 xxxxx 替换成您自己的 VPC ID,将 us-west-2 替换成您的 region)。

   ■ CDK_DEPLOY_REGION=us-west-2 yarn deploy serverless-ecs-image-handler-stack -c use_vpc_id=xxxxx -c use_bucket=your-bucket(此命令会基于一个已有的 S3 bucket 来部署,请将 xxxxx 替换成您自己的 VPC ID,将 us-west-2 替换成您的 region,将 your-bucket 替换成您的 S3 桶名称)。
  • 如果要删除部署的方案,请执行 yarn destroyserverless-ecs-image-handler-stack,如果要继续使用部署的方案,此步无需执行。

 部署结果

  • 当部署完成后,在命令行界面,会有类似如下的输出:

serverless-ecs-image-handler-stack.serverlessecrimagehandlerstackCFDistributionUrl1454FE90= https://ABCDEFGH.cloudfront.net

serverless-ecs-image-handler-stack.serverlessecrimagehandlerstackServiceLoadBalancerDNSDB026A6D= serve-serve-ABCDEF.us-west-2.elb.amazonaws.com

serverless-ecs-image-handler-stack.serverlessecrimagehandlerstackServiceServiceURLE05B511A= http://serve-serve-ABCDEF.us-west-2.elb.amazonaws.com

serverless-ecs-image-handler-stack.serverlessecrimagehandlerstackSrcBucketS3Url593801C5= s3://serverless-ecr-image-han-serverlessecrimagehandle-ABCDE


向上面输出的结果中最后一行(红字)的 S3 桶中上传一些图片,然后访问上面结果中第一行(红字)所输出的 CloudFront 地址,即可看到图片处理的效果,具体内容详见下一部分介绍。
 
对于 Lambda 的实现方式,目前我们提供基于 CloudFormation 的部署方式,该部署方式还处于测试阶段,因此目前用户无法直接看到部署操作步骤,有需要的用户可以联系我们提供测试版的 CloudFormation Template。






功能使用







本解决方案实现了丰富的图片处理 API,例如图片的水印、虚化、裁剪、质量压缩等等,有的功能可以在代码库找到对应实现,可以联系我们获取代码库地址。

当你部署成功后,可以直接通过 Restful API 的形式进行调用。调用 API 的设计格式如下:
http://<servie ip>:<port>/<processimage>?x-oss-process=image/<action name>,<attributename>_<attribute value>,<attribute name>_<attribute value>

例如以水印操作为例:
http://18.179.34.36:8080/example.jpg?x-oss-process=image/watermark,g_nw,auto_1,color_000000,size_40,fill_0,order_0,t_50,interval_0,x_30,y_10,text_5Lit5Zu95Lq65rCR,rotate_10






功能的扩展及二次开发







有的时候,你也许在方案中找不到你希望的图片处理操作,这个时候你可以考虑进行二次开发对功能进行扩展。你可以按如下形式开发新的图片处理方法:


  1. 请联系我们获取代码库,并 fork 代码

  2. 在 new-image-handler 项目的 image 文件夹下创建你的代码文件,文件名称为你的图像处理名称

  3. 继承接口 IImageAction .

  4. 实现 接口的 name 属性,该属性定义了操作的名称

  5. 实现接口 IImageAction 方法 validate, 该方法 主要负责对传入的参数进行验证。传入的参数满足 <attribute name>_<attribute value> 的格式,例如 size_10,它的含义是参数 size 的值为 10

  6. 实现接口 IImageAction 方法 process, 该方法 主要负责完成实际的图片处理逻辑。该方法的参数 ctx 的 image 对象是待处理图片的 sharp.js 对象。所以您可以在process 方法利用 sharp.js 的 api 对图片进行操作

  7. 在 index.ts 文件中通过 ImageProcessor.getInstance() 注册您的处理对象


代码框架对整个图像处理逻辑进行了较高层次的抽象,所以实现一个自己的图片处理操作是比较简单的。

在开发过程中,你可能希望在你的本地环境进行测试,这也很容易。你只需要在你的开发环境部署好 docker, 然后直接以容器的方式启动,就可以在本地测试你的 api。

在 build docker镜像之前,你需要对根目录下的 Dockerfile 进行适当修改:

  1. 修改代码根目录下的 Dockerfile, 将
    ENV NODE_ENV=production
    # 修改为
    ENV NODE_ENV=dev
  2. 取消 Dockerfile 第 61 行的注释。这行代码会把一些测试图片复制到docker容器中,供测试使用。
    # COPY test/fixtures app/lib/test/fixtures

该方案通过 sharp.js 对图片进行操作,并且在图片处理中大部分场景,sharp.js 都提供了对应的函数,用户可以直接调用,传入必要的参数即可实现,唯独水印功能,暂时没有直接的函数实现。而 添加水印功能是比较常见的图片处理需求了,所以在这里我们以实现一个水印功能,让你了解一下 sharp.js 的使用 ,便于你以后更容易的在该框架下实现自己的图片处理过程。
 
sharp.js 在图像合成方面虽然只提供了一个函数:composite,但它十分强大。它接受一个由 OverlayOptions 的对象组成的列表作为参数。每个 OverlayOptions 对象接受一个图片参数(可以是图片本地路径字符串或者图片 Buffer 数据)以及一些可选的配置项。 


最常见的几个配置项如下:
  1. blend:图片的混合方式,默认值为 'over',还有其他常用的值为 'overlay','out'。不同的值有不同的混合效果,需要通过尝试选择你需要的混合方式

  2. gravity:水印图片/或文字相对底图的布局方式,默认为 center, 另外常用的值有 north,northwest

  3. top,left:分别是相对于上边和左边的像素偏移量

  4. tile:水印图片/或文字是否需要重复,默认值 false


水印功能,一般还包括对被混合图片/文字的旋转,布局方式,偏移,不透明度属性的修改,文字水印需要能设置字体大小,字体颜色等属性,图文混合水印需要设置图片文字的对齐方式,图片文字间隔等等。


我们先看看如何实现一个简单的图片水印:

const sharp = require('sharp');


(async () => {
    let watermarkImg = sharp('panda.png');
    const overlay = await watermarkImg.toBuffer()
    const background = sharp('aws.png')
    let u = await background
        .composite([
            { input: overlay, blend: 'over'}
        ]).toFile('./output/o4.png');
})().catch(console.error)

如下分别是 blend 设置为:over、overlay、multiply 的效果:


纯文字水印的实现要依赖 svg:

const sharp = require('sharp');

const color = `#FFFF66`;
const opacity = 0.8
const text = 'hello, 伟大的祖国'
const svg = `<svg xmlns="http://www.w3.org/2000/svg"viewBox="0 0 600 80" text-anchor="middle">
<text font-size='60'  x="300" y="60"fill="${color}" transform="rotate(10)"opacity="${opacity}">${text}</text>
</svg>`;

(async () => {
    const overlay = Buffer.from(svg);

    const background = sharp('aws.png')
    let u = await background
        .composite([
            { input: overlay, blend: 'over',gravity: 'northwest', top: 10, left: 20 }
        ]).toFile('./output/o7.png');
})().catch(console.error)


上面代码对 svg 内的文字进行 rotate transform,想以此方式实现文字水印的旋转,但是这样出现如下图的 bug,部分文字被截取。原因是这种方式只会修改文字相对 svg 的旋转角度,而 svg 本身还是水平放置。
 
所以要实现对水印文字进行旋转,你需要先将文字转换成图片,对上面代码进行如下修改:
const sharp = require('sharp');

const color = `#FFFF66`;
const opacity = 0.8
const text = 'hello, 伟大的祖国'

const svg = `<svg xmlns="http://www.w3.org/2000/svg"viewBox="0 0 600 80" text-anchor="middle">
<text font-size='60'  x="300" y="60"fill="${color}"opacity="${opacity}">${text}</text>
</svg>`;

(async () => {
    const overlay = Buffer.from(svg);
    let overlapImg = sharp({
        create: {
            width: 600, 这里需要保证转换后的图片和文字对应的svg尺寸一致
            height: 80,
            channels: 4,
            background: { r: 0, g: 0, b: 0,alpha: 0 },
        },
    }).composite([{ input: overlay }]);
    const overlapImgBuffer = await overlapImg.png().toBuffer();
    overlapImg = sharp(overlapImgBuffer).png();

    overlapImg = overlapImg.rotate(10, { background: '#00000000'});
    const watermarkImgBuffer = await overlapImg.toBuffer();

    const background = sharp('aws.png')
    let u = await background
        .composite([
            { input: watermarkImgBuffer,blend: 'over', gravity: 'northwest', top: 10, left: 20 }
        ]).toFile('./output/o7.png');
})().catch(console.error)


两次的实现的效果如下:


有的时候你需要调整水印的透明度,文字水印比较简单,你可以如上面代码修改 svg 的 opacity 实现,而图片水印相对有些麻烦。例如对于一张 jpg 图片,你可以这么修改水印的透明度:

const opacity = 0.8
overlapImg.removeAlpha().ensureAlpha(opacity);


但是对于 png 图片,这样做会造成如下效果:


大家可以看到,png 原本透明的部分被填充成了黑色。所以针对 png 图片,你应该使用卷积函数来实现透明度的修改:

const sharp = require('sharp');

const opacity = 0.5;

(async () => {
  let watermarkImg = sharp('panda.png').png();
  const bt = await watermarkImg.convolve({
    width: 3,
    height: 3,
    kernel: [
      0, 0, 0,
      0, opacity, 0,
      0, 0, 0
    ]
  }).png().toBuffer();

  const background = sharp('aws.png')
  let u = await background
    .composite([
      { input: bt, blend: 'over' }
    ]).toFile('./output/o3.png');
})().catch(console.error)


这样效果就符合预期了:


水印功能的完整实现,你可以在代码库里找到完整实现。相信通过这个水印功能的实现,大家会更加熟悉 sharp.js 的使用,方便你实现更多的自定义图片处理操作。






费用分析







部署本方案后,会产生相关费用,为了给用户一个关于成本的直观概念,接下来我们以亚马逊云科技中国(宁夏)区域为例,来分析一下使用本方案的费用问题。

以下计算基于如下假设:
  • 每月新增图片数量分别为 10,0000、100,0000、500,0000,每张图片大小为 1MB。

  • 每次 Lambda Function 调用运行时间 2 秒,运行内存大小 1GB,无预置并发。

  • 每张图片上传 S3 存储桶后,会被用户请求 10 次,其中第 1 次处理回源,后 9 次命中缓存。

  • Fargate 计费时间单位为秒,不足一分钟按一分钟计。


基于上述数据,采用 Lambda Function 实现方式每月产生的费用为(以人民币计):


采用 ECS Fargate 实现方式的方案每月产生的费用为(以人民币计):







结论







基于亚马逊云科技的若干基础服务,如 S3、API Gateway、CloudFront、ECS Fargate 等,我们构建了一套图片处理的 API 解决方案,使得用户在使用可靠的对象存储服务的时候,能够获得更多更高的附加值。在诸如在线家居设计、在线多媒体文件制作、在线游戏等领域,用户使用该方案可以更加快速、经济、可靠地构建自己的产品。


本方案方案基于 Amazon Lambda、Amazon API Gateway、ECS Fargate 等无服务架构,用户无需担心在云中或本地管理和运行服务器或运行时,只需按实际使用量支付费用,这也极大的减轻了客户的运维负担。


基于 CloudFront 进行用户请求的响应,也使得方案可以快速地服务全球不同地理分布的终端用户,使得产品具有更好的用户体验。


方案设计时也参考了市场上相关竞品,使得最终的方案也可以很好地服务从别的云平台迁移过来用户,减少他们的迁移工作量和上手适应时间,能够集中精力开发自己的新业务。未来该方案还会持续演进,敬请关注。




作者介绍

宋孜攀

西云数据解决方案架构师

10+ 年软件研发及解决方案架构咨询经验,曾就职于世界 500 强和初创软件企业,对软件研发、云计算技术有深入了解和丰富经验。

许庭新

西云数据解决方案架构师

10+ 年产品研发和解决方案咨询经验,在电商、互联网金融、智能汽车领域有丰富的实战经验,擅长利用云计算、大数据、AI 等技术挖掘用户底层需求,实现精准运营。


互动


参与分享活动,赢取精选好礼!根据 “分享最多” 排行榜,我们将在每月第一次推送中公布获奖名单,为前 10 名送上精美礼品。







文章转载自西云数据云计算,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论

暂无图片
1年前
评论
暂无图片 0
你好,代码库是哪个啊?
1年前
暂无图片 点赞
评论