国内的云存储厂商(如阿里云,七牛云等)提供了比较方便的图片处理方案,只需要添加不同的图片访问参数值,就可以实现图片的缩放。AmazonS3并没有提供现成的方案,但我们可以通过Cloudfront+Lambda@Edge实现图片的缩放功能。
Lambda@Edge
Lambda@Edge可以认为是运行在Cloudfront上的Lambda,不同的是Lambda@Edge只支持Nodejs
和Python
两种语言,而且Lambda@Edge仅可在 美国东部 (弗吉尼亚北部) us-east-1 配置。
我们可以在客户端访问Cloudfront的4个过程中配置Lambda:
- 查看器请求(Viewer Request):在检查是否命中Cloudfront缓存前执行,就是客户端每个请求都会执行
- 源请求(Origin Request):当Cloudfront缓存未命中,并且请求未到达源时执行
- 源响应(Origin Response):当Cloudfront缓存未命中,并且得到源响应后执行
- 查看器响应(Viewer Response):当Cloudfront命中缓存或者得到了源响应后执行,就是每个客户端请求都会执行
方案架构
Lambda配置
我们在这里配置了2个Lambda函数:
1.配置于源请求,用于检查请求图片地址是否合法,判断是否需要修改请求。
2.配置于源响应,用于生成缩略图。
配置于源请求的Lambda函数其实是可以省略的,此处我们希望缩略图能够统一存储,所以此函数主要用于修改请求地址,例如客户端请求图片为/a/b/c_200x200.jpg
,在Lambda函数处理后,请求至S3的地址会修改为/thumbnails/200x200/a/b/c.jpg
,将所有缩略图存储在thumbnails
文件夹下。
流程说明
1.客户端请求Cloudfront
2.Cloudfront上未命中缓存,请求S3之前执行Lambda函数,修改请求地址
3.获取S3响应。如果S3返回图片不存在,执行Lambda
4.Lambda下载原图并生成缩略图,转存至S3,将图片返回给Cloudfront
5.Cloudfront返回结果给客户端
Lambda函数
代码地址:https://github.com/iyichen/lambda-thumbnail
源请求函数
1 | ; |
源请求函数中,我们定义了缩略图的规范(只允许200x200
和75x75
),避免生成一些无意义的缩略图,当符合请求规则后,将请求地址修改。如请求地址为/images/image_200x200.jpg
,经过函数处理后,实际请求S3的地址为/thumbnails/200x200/images/image.jpg
源响应函数
1 | ; |
在源响应函数分成以下几步:
1.通过源响应的返回判断图片是否存在
2.如果图片不存在,解析请求地址,获取原图地址和尺寸
3.下载原图,生成缩略图,将缩略图传至S3
4.返回缩略图给Cloudfront
其他
设置生命周期
可以对缩略图配置生命周期,到期自动删除,可以节省存储空间
源响应函数中response.status=403
在S3中,如果用户只有s3:GetObject
权限,即使文件不存在,S3出于安全默认返回的也是403;如果需要返回404状态,需要赋予s3:ListBucket
权限。
Lambda提示Sharp报错
按照官网说明,指定Lambda环境为nodejs10.x
,修改安装命令:
1 | rm -rf node_modules/sharp |
Sharp输出GIF格式报错
sharp
不支持gif
,示例代码中是取gif
第一帧图片,缩放后图片虽然后缀为.gif
,其实格式为png
参考
Resizing Images with Amazon CloudFront & Lambda@Edge
Lambda@Edge Design Best Practices
Lambda@Edge开发人员指南
Leveraging external data in Lambda@Edge
Lambda@Edge gotchas and tips
Building a Real-Time Image Optimizer Using Lambda@Edge and Amazon CloudFront