一、背景
用户上传的原图需要以缩略图格式展示,如果每次都加载原图的话比较耗费时间。所以考虑使用png格式的缩略图进行处理。对此官方推荐使用Lambda@Edge对原图进行处理。官方参考文档:https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-at-the-edge.html
网上博主的参考文档:https://aws.amazon.com/cn/blogs/china/use-cloudfront-lambdaedge-for-transaction-processing/
二、配置cloudfront
1.1源站为s3
1.2配置cf行为
备注:可以配置多个行为,例如模糊匹配jpg png等,注意无论原图是哪种格式之后切的图片格式都是png格式。/*.jpg 表示匹配所有jpg文件
1.3配置缓存、源请求策略
备注:如果选择旧缓存设置则字符串策略要选择字符串永远匹配
三、配置lambda
3.1上传zip文件
备注:cloudfront-lambda-s3-picture.zip py文件改成lambda_function.py
import json
import boto3
import PIL
from PIL import Image
from io import BytesIO
import os
from urllib.parse import parse_qs
import base64
# All the buketname and dir name should be lowercase to meet the s3 naming requirements.a
bucketName = 'g6p-release'
rawDir = 'test-ops'
Cloudfront_Origin_Path='/test-ops-origin/'
PUT_TO_S3='FALSE'
def lambda_handler(event, context):
# Get object
cf = event['Records'][0]['cf']
request = cf['request']
params = {k: v[0] for k, v in parse_qs(request['querystring']).items()}
image = params.get('image')
size = params.get('size')
rawObject = "{dir}/{key}".format(dir=rawDir, key=image)
print('rawObject:',rawObject)
response = ''
#
if 'response' in cf:
trigger_point = 'RESPONSE'
else:
trigger_point = 'REQUEST'
if trigger_point == 'REQUEST':
buffer = resize_s3_image(bucketName, rawObject, size)
# If triggerd by Original Rqeust, you may want to conitune forward to original server, so you should upload the file back to s3 then return request.
# For example, if your client does not accept base64 encoded picture files, you need to do so, In this case you can't use the original response trigger
# return request
# Otherwise, return respone to viewer directly like flow:
return generate_response(response, buffer, trigger_point)
if trigger_point == 'RESPONSE':
response = cf['response']
if int(response['status'])>= 400 and int(response['status']) <= 599:
buffer = resize_s3_image(bucketName, rawObject, size)
# You can decide whether to send the file back to s3 so that subsequent get operations can get the existing file directly .
if PUT_TO_S3 == 'TRUE':
uri = request['uri']
newObject = "{rootpath}{ouri}".format(rootpath=Cloudfront_Origin_Path.strip('/'), ouri = uri)
upload_to_s3(bucketName, newObject, buffer)
return generate_response(response, buffer, trigger_point)
else:
return response
def resize_s3_image(bucket_name, objectKey, size):
size_split = size.split('x')
s3 = boto3.resource('s3')
obj = s3.Object(
bucket_name = bucket_name,
key = objectKey,
)
obj_body = obj.get()['Body'].read()
img = Image.open(BytesIO(obj_body))
img = img.resize((int(size_split[0]),int(size_split[1])),PIL.Image.ANTIALIAS)
buffer = BytesIO()
img.save(buffer,'png')
buffer.seek(0)
return buffer
def upload_to_s3(bucket_name, objectKey, buffer):
# Upload the generated objcect to s3
s3 = boto3.resource('s3')
obj = s3.Object(
bucket_name = bucket_name,
key = objectKey,
)
obj.put(Body=buffer,ContentType='image/png')
# Construct Resopne.
def generate_response(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'
#Json cannot serialize binary picture files, so first force-code them with base64 into text files that json thinks can be serialized, and then reverse-code at the browser end
buffer.seek(0)
response['body'] = base64.b64encode(buffer.read()).decode()
response['bodyEncoding'] = 'base64'
response['headers']['content-type'] = [{'key': 'Content-Type', 'value': 'image/png'}]
response['headers']['content-encoding'] = [{'key': 'Content-Encoding', 'value': 'base64'}]
if trigger_point == 'REQUEST':
response['statusDescription'] = 'Generated by CloudFront Original Request Function'
if trigger_point == 'RESPONSE':
response['statusDescription'] = 'Generated by CloudFront Original Response Function'
return response
3.2配置触发器
3.3触发器选择对应cf id
3.4lambda函数超时时间修改
超时时间改成30秒
四、测试
备注:可以根据自身需求调整缩略图大小,lambda第一次处理时时间有点长大概有6s,而切好的图片缓存中保存的时间为一天。
五、总结
- 源码来着网上,有些东西还是被限制了,比如定义缩略图目录(变量rawDir只能是字符串,不能为空且不能写"/")
- lambda每次更新完要重新部署
- 触发器获取事件是源响应模式,所以原图访问不受影响,缩略图的话就通过加args参数进行获取
- 对于uri字符串ops-test21212121.png表示切割后的图片,ops-test-2.jpg表示原图
六、附带nginx的配置文件
如果大陆想通过cn2服务器中转请求的话,该场景比较合适
[root@g6p.cn vhost]# egrep -v "#|^$" oss.g6p.cn.conf
server {
listen 80;
server_name oss.g6p.cn;
access_log /data/wwwlogs/oss-cn-shenzhen.aliyuncs.com.log;
location / {
proxy_set_header host $http_host;
proxy_set_header Host "d13c0gm99vxgzj.cloudfront.net";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header HTTP_X_FORWARDED_FOR $remote_addr;
proxy_pass http://d13c0gm99vxgzj.cloudfront.net;
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico|js|css|webp)$ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
expires 30d;
access_log off;
proxy_cache cache_one;
proxy_pass http://d13c0gm99vxgzj.cloudfront.net;
}
}
server {
listen 443 ssl;
ssl_certificate vhost/g6p.cn.pem;
ssl_certificate_key vhost/g6p.cn.key;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
access_log /data/wwwlogs/oss-cn-shenzhen.aliyuncs.com_ssl.log;
error_log /data/wwwlogs/oss-cn-shenzhen.aliyuncs.com-error_ssl.log debug;
server_name oss.g6p.cn;
location / {
proxy_set_header Host "d13c0gm99vxgzj.cloudfront.net";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header HTTP_X_FORWARDED_FOR $remote_addr;
proxy_pass http://d13c0gm99vxgzj.cloudfront.net;
proxy_cache cache_one;
proxy_cache_key $host$uri$is_args$args;
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico|webp|woff)$ {
expires 3d;
proxy_set_header Host "d13c0gm99vxgzj.cloudfront.net";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
access_log off;
proxy_cache cache_one;
proxy_pass http://d13c0gm99vxgzj.cloudfront.net;
}
}
留言