1、主要介绍内容
此篇文章主要利用Aliyun OSS Nginx proxy module 实现OSS 图片处理回写功能,借助OSS Nginx Proxy module 及 OSS 的上传回调功能实现OSS图片处理回写功能,当然文章目的并不在于强调图片处理回写功能,而是借实现一个例子来利用Aliyun OSS Nginx proxy module,对Nginx 及 Nginx lua 感兴趣的同学可以参照本文描述做出更强大的应用出来,本文如能抛砖引玉的作用那就不枉我花时间写此博客了。
2、开始之前
1、开始之前请稍微阅读下对Aliyun OSS Nginx proxy module 的简单介绍
https://yq.aliyun.com/articles/8532?spm=0.0.0.0.EZk4yy
2、了解下阿里云OSS 上传回调功能
3、参考文章
在开始之前建议阅读本文之前建议阅读以上两篇参考文档
春哥的Nginx教程,会对Nginx 配置文件有很深的理解
http://openresty.org/download/agentzh-nginx-tutorials-zhcn.html
我的另外一篇博客,会更加理解OSS 提供的服务
https://yq.aliyun.com/articles/7511?spm=0.0.0.0.IWbHSR
3、主要功能及优势
本文主要实现的功能是处理回写,这里以oss图片处理回写为例介绍如何利用阿里云OSS 提供的callback来进行图片回写。现在很多大图片如果每次都要实时处理那会大大降低用户体验(当然可以利用CDN进行缓存),那么可以通过此方法进行处理图回写,每次进行上传请求时带上callback参数让oss通知到本文中实现的nginx服务器,nginx服务器会自动从oss 拉取经过处理的数据然后再回写带oss中,如果确认成功甚至可以将原图删掉只保留处理图,这些都可以依据上传逻辑进行实现。那么肯定会有读者问那费用呢?这个完全不是问题,因为oss按照请求次数收费相当便宜几乎不要钱,而流量费用也不用担心,买一台和OSS同区域的ECS就可以走内网了,内网流量免费。
4、图片处理回写处理逻辑代码
oss_rewrite.lua
local cjson = require("cjson")
local function get_object_data_from_oss_img(src_object, src_style)
local res = ngx.location.capture("/" .. src_object .. src_style)
if res.status ~= ngx.HTTP_OK then
ngx.log(ngx.ERR, "fetch external url data failed, object :", src_object .. src_style, " status:", tostring(res.status))
return res.status, nil
end
return res.status, res.body
end
local function get_and_process(json_table)
src_object = json_table["src_object"]
src_style = json_table["style"]
dst_object = json_table["dst_object"]
ngx.log(ngx.INFO, "src_object:", src_object, " style:", src_style, " dst_object:", dst_object)
if src_object == nil or src_style == nil or dst_object == nil then
ngx.log(ngx.ERR, "src_object style and dst_object should be not null")
return ngx.HTTP_BAD_REQUEST
end
-- Get processed img data from oss
local status, write_back_body = get_object_data_from_oss_img(src_object, src_style)
if status ~= ngx.HTTP_OK then
return status
end
-- Write back
local res = ngx.location.capture("/" .. dst_object, {
method = ngx.HTTP_PUT,
body = write_back_body
})
if res.status ~= ngx.HTTP_OK then
ngx.log(ngx.ERR, "write back to oss failed, object:", dst_object, "status:", tostring(res.status))
return res.status
end
return ngx.HTTP_OK
end
local function parse_and_process_post_body()
ngx.req.read_body()
local post_json_body = ngx.req.get_body_data()
ngx.log(ngx.INFO, post_json_body)
if post_json_body == nil then
ngx.log(ngx.ERR, "post request body from oss call_back is empty.")
return ngx.HTTP_BAD_REQUEST
end
-- decode json string and catch json errors
local succ, json_table = pcall(function()
return cjson.decode(post_json_body)
end)
if succ then
return get_and_process(json_table)
else
ngx.log(ngx.ERR, "post request body from oss call_back is not json.")
return ngx.HTTP_BAD_REQUEST
end
end
local method = ngx.req.get_method()
if method == 'GET' or method == "POST" then
local status = parse_and_process_post_body()
if status == ngx.HTTP_OK then
ngx.say("{\"success\": \"true\"}")
else
ngx.log(ngx.ERR, "fail write back to oss")
ngx.exit(status)
end
else
ngx.log(ngx.INFO, "do not support this method ", method)
ngx.exit(ngx.HTTP_BAD_REQUEST)
end
nginx.conf
error_log logs/error.log debug;
events {
worker_connections 1024;
}
http {
include mime.types;
lua_package_path "/usr/servers/lualib/?.lua;";
lua_package_cpath "/usr/servers/lualib/?.so;";
server {
listen 80;
server_name your-server-name; # 注意此项要和CallBack 参数中的Host 保持一致
resolver 8.8.8.8;
#lua_code_cache off; # 加上此配置在每次修改lua 代码后无需reload nginx
location /oss_write_back {
content_by_lua_file conf/lua/oss_rewrite.lua;
}
location / {
internal; #只允许内部跳转
set $oss_bucket "you-oss-bucket";
set $oss_auth_id "you-access-key-id";
set $oss_auth_key "you-access-key-secret";
rewrite_by_lua_file conf/lua/oss_auth.lua;
}
location @oss {
#eg: bucket-example.oss-cn-qingdao.aliyuncs.com
proxy_pass http://$oss_bucket.oss-location.aliyuncs.com;
}
}
}
5、如何使用上述代码及构造OSS callback 请求
1、准备环境配置文件
自己搭建openresty 环境或者直接pull docker 官方镜像源openresty镜像
将oss_rewrite.lua 放到conf/lua/oss_rewrite.lua 中
将http://blog.csdn.net/sunrain_chy/article/details/50935681 提到的oss_auth.lua 放到conf/lua/oss_auth.lua 中
配置好nginx.conf 文件,启动nginx
2、构造Callback 请求
为了演示方便将bucket 权限设置为public-read-write并利用http://blog.csdn.net/sunrain_chy/article/details/50804410这篇文章介绍的方法进行请求构造
构造PUT 请求 call back 参数
callback.txt
{
"callbackUrl":"your-call-back-url/oss_write_back",
"callbackHost":"your-call-back-host",
"callbackBody":"{
\"src_object\":\"your-src-object-name\",
\"style\":\"style\",
\"dst_object\": \"your-dst-object-name\"
}",
"callbackBodyType":"application/json"
}
注意这里的your-call-back-host 要与nginx.conf 中的server_name 保持一致,回调url 的location 要是oss_write_back
src_object 为oss上存在的一张图片object,通常设置为此次PUT 请求上传的Object
style 为 oss 图片处理参数
dst_object 为处理图回写到OSS 的objectname
对上述callback.txt 进行base64编码
$ base64 callback.txt | tr -d '\n'
3、构造带CallBack参数的PUT 请求
PUT /test.jpg HTTP/1.1
Host: bucket-example.oss-ch-qingdao.aliyuncs.com
Accept-ncoding: identity
Content-Length: 10584
x-oss-callback-var: eyJ4Om15X3ZhciI6ImZvci1jYWxsYmFjay10ZXN0In0=
x-oss-callback: ewogICAgImNhbGxiYWNrVXJsIjoiMzAuOS4xNjkuNDIvb3NzX3dyaXRlX2JhY2siLAogICAgImNhbGxiYWNrSG9zdCI6IjMwLjkuMTY5LjQyIiwKICAgICJjYWxsYmFja0JvZHkiOiJ7CiAgICAgICAgICAgIFwic3JjX29iamVjdFwiOlwidGVzdC5qcGdcIiwKICAgICAgICAgICAgXCJzdHlsZVwiOlwiQDEwMHdfMTAwaFwiLAogICAgICAgICAgICBcImRzdF9vYmplY3RcIjogXCJ0ZXN0LmpwZ18xMDB4MTAwXCIKICAgICAgICB9IiwKICAgICAgICAiY2FsbGJhY2tCb2R5VHlwZSI6ImFwcGxpY2F0aW9uL2pzb24iCn0K
...imgdata
注意上述x-oss-callback 参数为callback.txt 的base64编码
4、利用nc 将带call back 参数的请求发往oss
5、结果检测
在上述参数配置无误的情况下bucket中会多出来一个名为your-dst-object-name的object,这个object正是由your-src-object-name 带上style 参数经过OSS图片处理过的object。
6、程序运行主线
1、用户发送带callback参数的PUT请求到OSS,OSS根据callback提供的参数会调用用户的应用服务器,也就是我们的nginx
2、nginx 接收到这个请求后解析body参数获取到 源object 处理参数及回写object名,这一步走的是nginx conf 中的 location /oss_write_back
3、Nginx 根据解析出来的post 参数发出一个内部跳转的子请求到 location / 去OSS取出处理参数为style 的处理图,当然取图请求会经过oss_auth.lua 走到location @oss进行签名
4、取出处理图后nginx lua 发出自请求将处理图数据回写到OSS至此回写完成。