当前位置: 首页 > news >正文

aws S3利用lambda edge实现图片缩放、质量转换等常规图片处理功能

前言

  • 与阿里的oss不同的是S3不支持通过url参数实现这种类似黑盒的图片处理,而是提供了一种特殊的lambda函数,我虽然不清楚为什么叫做lambda函数,但其本质就是一个拦截器。下面就演示一下利用lambda函数实现图片缩放和质量转换。

  • cloudfront是什么?这个是一个缓存服务,就是经常听说的cdn

  • 有四个阶段可以做图片处理,下文选择的是在origin request事件中做图片处理。
    在这里插入图片描述

有几个前置步骤,这里只列出,不做细致演示。

  • aws账号注册,注册后需要绑定一张信用卡才能使用。新注册用户有12个月的免费资源使用。从绑定银行卡之日起计算。
  • 创建存储桶,这个和阿里OSS一样。

步骤一:配置cloudfront(cdn)

  • 创建地址:https://us-east-1.console.aws.amazon.com/cloudfront/v4/home?region=us-east-1#/distributions
  • 注意cloudfront必须配置在佛吉尼亚北部
    在这里插入图片描述
    在这里插入图片描述
    这里需要注意一下, 启用了防火墙之后价格是按照请求另外计算的,防火墙不在免费资源范围内
    在这里插入图片描述
    创建完成之后,回到首页
    在这里插入图片描述
    点进去之后,这里就可以看到域名了。尝试使用该域名访问:https://图片中的域名/resized-boke.jpg
    在这里插入图片描述
    现在cloudfront就基本配置完成了。

步骤二: 创建lmabda edge函数

  • 创建地址:https://us-east-1.console.aws.amazon.com/lambda/home?region=us-east-1#/discover
    在这里插入图片描述
    创建函数时注意选择蓝图(这个一看就是机器翻译的,其实就是模板的意思)。执行角色这个不管选择哪个,后面都要去配置一下角色权限(https://us-east-1.console.aws.amazon.com/iam/home?region=us-east-1#/roles),防止出现执行函数异常的问题,或者日志不打印的问题。
    在这里插入图片描述
    注意一路向下部署完成后,此时版本1已经生效,回到函数页面点击更新函数,编写代码。
    在这里插入图片描述

步骤三:编写lambda函数代码:

import {GetObjectCommand, S3Client} from '@aws-sdk/client-s3';
import sharp from 'sharp';
import {Buffer} from 'buffer';
import {Readable} from 'stream';
import {parse} from 'querystring';const bucketName = 'chengzhi-test-resized';/*** 多个处理参数使用&符号分隔: /test.png@resize=50x50&width=50&height=50&proportion=50&quality=50* 图片处理参数类型:* - 按照像素缩放:    @resize=50x50* - 按照宽度等比缩放: @width=20* - 按照高度等比缩放: @height=200* - 按照比例缩放:    @proportion=99* - 质量转换:       @quality=50* @param event* @param context* @returns*/
export const handler = async (event, context) => {const cf = event.Records[0].cf;const request = cf.request;const params = getParams(request.uri);if (params === undefined) {console.log("未携带处理参数,不进行处理!");} else {let response = '';const image = params.image;const objectKey = `${image}`;const trigger_point = cf.response ? 'RESPONSE' : 'REQUEST';if (trigger_point === 'REQUEST') {let buffer = await getObjectBuffer(bucketName, objectKey);if (buffer === undefined) {return request;}if (params.resize) {buffer = await resizeImage(buffer, params.resize);}if (params.width) {buffer = await widthImage(buffer, params.width);}if (params.height) {buffer = await heightImage(buffer, params.height);}if (params.proportion) {buffer = await proportionImage(buffer, params.proportion);}if (params.quality) {buffer = await qualityImage(buffer, image.split('.')[1],params.quality);}return generateResponse(response, buffer, trigger_point);}}return request;
};async function getObjectBuffer(bucket_name, objectKey) {const region = "eu-north-1";const s3 = new S3Client({region: region});try {const params = {Bucket: bucket_name,Key: objectKey};var response = await s3.send(new GetObjectCommand(params));var stream = response.Body;if (stream instanceof Readable) {var content_buffer = Buffer.concat(await stream.toArray());return content_buffer;} else {throw new Error('Unknown object stream type');}} catch (error) {console.log(error);return;}
}async function heightImage(content_buffer, height) {try {let pipeline = sharp(content_buffer);height = Number(height);let resizeConfig = {height: height};pipeline = pipeline.resize(resizeConfig);return await pipeline.toBuffer();} catch (error) {console.log(error);return;}
}
async function widthImage(content_buffer, width) {try {let pipeline = sharp(content_buffer);width = Number(width);let resizeConfig = {width: width};pipeline = pipeline.resize(resizeConfig);return await pipeline.toBuffer();} catch (error) {console.log(error);}
}
/*** 等比例缩放图片* @param content_buffer* @param proportion       百分比(0-100)* @returns {Promise<*>}*/
async function proportionImage(content_buffer, proportion) {try {let pipeline = sharp(content_buffer);const metadata = await pipeline.metadata();proportion = Number(proportion);const percentage = proportion / 100;let resizeConfig = {width: Math.round(metadata.width * percentage)};pipeline = pipeline.resize(resizeConfig);return await pipeline.toBuffer();} catch (error) {console.log(error);}
}
/*** 裁剪图片* @param content_buffer* @param size            例如:50x40* @returns {Promise<*>}*/
async function resizeImage(content_buffer, size) {try {const [width, height] = size.split('x').map(Number);var output_buffer = await sharp(content_buffer).resize(width,height).toBuffer();} catch (error) {console.log(error);return;}return output_buffer;
}/*** 图片质量转换* @param content_buffer* @param format            图片格式* @param quality           目标质量(0-100)* @returns {Promise<*>}*/
async function qualityImage(content_buffer, format, quality) {try {// 传入的quality为字符串quality = Number(quality);console.log('quality:', quality)console.log('format:', format)let pipeline = sharp(content_buffer);// 根据格式设置质量switch (format.toLowerCase()) {case 'jpeg':case 'jpg':pipeline = pipeline.jpeg({quality: quality,mozjpeg: true  // 使用 mozjpeg 优化});break;case 'png':pipeline = pipeline.png({quality: quality,compressionLevel: 9  // PNG 压缩级别 0-9});break;case 'webp':pipeline = pipeline.webp({quality: quality,lossless: false  // 有损压缩});break;case 'avif':pipeline = pipeline.avif({quality: quality,lossless: false});break;}console.log('质量转换完成!')var outputBuffer = await pipeline.toBuffer();console.log('流转换完成!')} catch (error) {console.log(error);return;}return outputBuffer;
}
function generateResponse(response, buffer, trigger_point) {if (trigger_point === 'REQUEST') {response = {status: '',statusDescription: '',headers: {'cache-control': [{key: 'Cache-Control',value: 'max-age=100'}],'content-type': [{key: 'Content-Type',value: 'image/png'}],'content-encoding': [{key: 'Content-Encoding',value: 'base64'}]},body: '',bodyEncoding: 'base64'};}response.status = '200';response.body = buffer.toString('base64');response.bodyEncoding = 'base64';response.headers['content-type'] = [{key: 'Content-Type',value: 'image/png'}];response.headers['content-encoding'] = [{key: 'Content-Encoding',value: 'base64'}];response.statusDescription = trigger_point === 'REQUEST'? 'Generated by CloudFront Original Request Function': 'Generated by CloudFront Original Response Function';return response;
}function getParams(image) {image  = image.startsWith('/') ? image.slice(1) : imageif (!image.includes('@')) {console.log("不包含@符号,不需要进行处理!");return;}var strings = image.split('@');if (strings.length < 2) {console.log("@符号位置不正确,不进行处理");return;}var result = parse(strings[1]);var picture = strings[0];if (picture === undefined || !picture.match(/\.(jpg|jpeg|png|gif|webp)$/i)) {console.log("非图片类,不进行处理");return;}if (result !== undefined) {result.image = picture;}console.log("图片处理参数:", JSON.stringify(result))return result;
}

函数编写完成后,需要将依赖打包,注意因为是要在aws的服务器上运行该代码,所以引入的依赖必须是linux版本的。以下是代码中使用到的两个依赖。

npm install --arch=x64 --platform=linux --target=16x sharp@0.32.6
npm install --arch=x64 --platform=linux --target=16x aws-sdk@2.1450.0 

依赖引入完成后,需要打包成zip包,包结构如图,如果zip包超过10M,上传会超时,需要使用S3上传,注意上传代码的S3也必须是弗吉尼亚北部。否则上传会失败。
在这里插入图片描述

代码上传完成后,一定要修改函数执行超时时间,否则函数运行1s后,就自动停止了。

在这里插入图片描述

步骤四:部署

代码完成后,点击添加触发器,选择cloudfront(注意如果函数不是在弗吉尼亚北部,这个是选择不到的),按照提示填写配置。注意我上面的代码cluodfront事件选择的是源请求
在这里插入图片描述

步骤五: 测试

https://域名/novel1.png@height=200
在这里插入图片描述
验证是否生成缓存
https://us-east-1.console.aws.amazon.com/cloudfront/v3/home?region=us-east-1#/popular_urls
在这里插入图片描述

答疑

  • 1、为什么要在url中使用@?
    答: 在测试过程中发现,函数中无法获取到queryString的参数,也就是说无法获取到url问号后面的参数部分,因此采用@然后在程序中做分割处理,这样一来可以解决获取参数的问题, 二来可以解决图片名称相同但尺寸不同的缓存问题。

  • 2、为什么要在源请求中做处理?
    答: 刚开始也考虑在源响应中做处理,但是发现源响应的response中并没有body,咨询了aws的售前说源响应是没有body的。
    在这里插入图片描述

  • 3、是否可以兼容ali oss图片处理参数?
    答:基于上述内容可以发现,lambda函数使用的是sharp库,这个库中支持的那大概率都能实现,我只是因为只需要用到这几个处理方式。

  • 4、为什么使用Node,不适用python?
    答: 刚开始尝试使用了python,但是打包上传到服务上时提示,有包冲突,谷歌了一下,说是需要用到aws的一个,个人觉比较复杂,就更换了node.

参考文档:

https://aws.amazon.com/cn/blogs/china/use-cloudfront-lambdaedge-for-transaction-processing/
https://docs.aws.amazon.com/zh_cn/lambda/latest/dg/getting-started.html#get-started-invoke-manually
https://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/lambda-at-the-edge.html
https://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/GettingStarted.SimpleDistribution.html
https://docs.aws.amazon.com/zh_cn/lambda/latest/dg/with-s3-tutorial.html
https://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/popular-objects-report.html


http://www.mrgr.cn/news/95874.html

相关文章:

  • Java 线程池全面解析
  • Linux输入系统应用编程
  • 【linux重设gitee账号密码 克隆私有仓库报错】
  • 3、孪生网络/连体网络(Siamese Network)
  • 【WebGIS教程1】WebGIS学习初步知识了解 · 概述
  • 2025最新版Ubuntu Server版本Ubuntu 24.04.2 LTS下载与安装-详细教程,细致到每一步都有说明
  • Linux--环境变量
  • 向量数据库学习笔记(1) —— 基础概念
  • djinn: 1靶场渗透测试
  • 微服务面试题:分布式事务和服务监控
  • 中学数学几百年重大错误:将无穷多各异假R误为R——两数集相等的必要条件
  • 万字C++STL——vector模拟实现
  • STM32内部时钟输出比较OC(学习笔记)
  • 常用的离散时间傅里叶变换(DTFT)对
  • Langchain中的表格解析:RAG 和表格的爱恨情仇
  • 深入 SVG:矢量图形、滤镜与动态交互开发指南
  • Python进阶编程总结
  • 定长内存池原理及实现
  • 【Linux知识】RPM软件包安装命令行详细说明
  • MoManipVLA:将视觉-语言-动作模型迁移到通用移动操作