利用阿里云OSS Nginx proxy module 实现OSS 图片处理回写功能

更新时间:2017-09-16点击: 技术分享

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 上传回调功能

https://help.aliyun.com/document_detail/oss/api-reference/object/Callback.html?spm=5176.docoss/user_guide/manage_object/list_object.6.238.SAPehH

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至此回写完成。


推荐阅读