搭建图片服务(三)

为图片服务增加jwt认证

安装依赖

  • lua-resty-jwt-0.1.11.tar.gz
  1. 解压
   tar -zxf lua-resty-jwt-0.1.11.tar.gz

  1. 安装
   cd cd lua-resty-jwt-0.1.11/lib/
   cp -R resty /usr/local/luajit/lib/lua
  • basexx
  1. 下载
  https://github.com/aiq/basexx/blob/master/lib/basexx.lua
  1. 安装
cp basexx.lua /usr/local/nginx/lua_lib

  • nginx-jwt.lua
  1. 下载
   https://github.com/auth0/nginx-jwt/blob/master/nginx-jwt.lua
  1. 安装
   cp nginx-jwt.lua /usr/local/nginx/lua_lib/
  1. 修改
   47行 local jwt_obj = jwt:verify(secret, token, 0)
   修改为 local jwt_obj = jwt:verify(secret, token)

配置环境变量

   export JWT_SECRET=4Tz7JuEERJkrIsU=
   export JWT_SECRET_IS_BASE64_ENCODED=true

配置nginx.conf

   location /upload {
        access_by_lua '
            local jwt = require "nginx-jwt"
            jwt.auth()
        ';
        error_log logs/upload_err.log;
        content_by_lua_file lua_lib/nginx_upload.lua;
   } 

完。

搭建图片服务(二)

图片写入Mongodb

所需软件

  • lua-resty-moongoo-master.zip
  • lua-cbson-master.zip
  • mongo-c-driver-1.7.0.tar.gz

依赖安装

  • 安装mongo-c-driver
  1. 解压mongo-c-driver-1.7.0.tar.gz
    tar -zxf mongo-c-driver-1.7.0.tar.gz
  2. 安装依赖
    yum install cyrus-sasl-devel
  3. 编译安装
    ./configure --enable-sasl=yes
    make && make install
  4. 链接库
    ln -s /usr/local/lib/libbson-1.0.so.0 /lib64/libbson-1.0.so.0
  • 安装lua-cbson
  1. 解压lua-cbson-master.zip

    unzip lua-cbson-master.zip

  2. 安装依赖

    yum install cmake gcc gcc-c++

  3. 编辑FindLuaJIT.cmake文件

   cd cmake
   vi FindLuaJIT.cmake
   在find_path和find_library加入/usr/local/luajit/ 
  1. 配置
    mkdir build && cd build && cmake ..

  2. 编译安装
    make && make install

  • 安装lua-resty-moongoo
  1. 解压lua-resty-moongoo-master.zip
    unzip lua-resty-moongoo-master.zip
  2. 安装
    cd lib && cp -R resty /usr/local/luajit/lib/lua/

上传脚本

  • mongo_write.lua
  • nginx_upload_for_mongodb.lua

修改nginx.conf

location /upload_to_mongodb {
    error_log logs/upload_err.log;
    content_by_lua_file lua_lib/nginx_upload_for_mongodb.lua;
}

重新载入

nginx -s reload

脚本内容

  • mongo_write.lua
local moongoo = require("resty.moongoo")
local cbson = require("cbson")

local _M = {}           -- 局部的变量
_M._VERSION = '1.0'     -- 模块版本
local mt = {__index = _M}

function _M:new(o,url,dbname)
    o = o or {}
    self.__index = self
    self.url = url
    self.dbname = dbname
    self.mg = nil
    setmetatable(o,self)
    return o
end

function _M:connection()
   local mg, err = moongoo.new(self.url)
   if not mg then
    error(err)
   end
   return mg
end

function _M:writeFile(filename,data,lng,lat)
   if self.mg == nil then
     self.mg = self:connection(self.url)
   end
   local gridfs = self.mg:db(self.dbname):gridfs()
   local fs = gridfs:create(filename,{filename=filename,date=os.date(),metadata={longitude=lng,latitude=lat}},false)
   fs:write(data)
   fs:close()
end

function _M:updateInfo(fns,metadata)
   if self.mg == nil then
     self.mg = self:connection(self.url)
   end
   local c = self.mg:db(self.dbname):collection("fs.files")
   for _,fn in ipairs(fns) do
      local fsFile = c:find_one({filename=fn})
      fsFile['metadata']= metadata
      c:update({filename=fn},fsFile)
   end
end

function _M:close()
   if self.mg ~= nil then
      self.mg:close()
   end
end

return _M

  • nginx_upload_for_mongodb.lua
string.split = function(line, sep)

    sep = sep or ' '
    local retval = {}
    local pos = 1
    while true do
        local from, to = string.find(line, sep, pos)
        if from then
            local item = string.sub(line, pos, from-1)
            table.insert( retval, item )
            pos = to + 1
        else
            local item = string.sub(line, pos)
            table.insert( retval, item )
            break
        end
    end
    return retval
end    

string.trim = function (s)
  return (s:gsub("^%s*(.-)%s*$", "%1"))
end

--获取扩展名  
function getExtension(filename)  
    return filename:match(".+%.(%w+)$")  
end

local stringy = require "stringy"
local resty_sha1 = require "resty.sha1"
local upload = require "resty.upload"
local cjson = require "cjson"
local chunk_size = 4096
local form = upload:new(chunk_size)
local sha1 = resty_sha1:new()
local file
local longitude='11111'
local latitude='22222'
local extnames={"png","jpg","jpeg","gif"}
local msg = {success=false,msg='上传失败',data=nil}


local mongo = require("mongo_write")
local mg = mongo:new(nil,"mongodb://user:password@ip:port/dbname?authMechanism=SCRAM-SHA-1",'dbname')

function extExists(ext)
    for _, value in pairs(extnames) do
        if value == ext then
            return true
        end
    end

    return false
end

local fileNames={}
local fileRes={}
local part_name, part_value
local params={}
local ids={}
while true do
    local typ, res, err = form:read()

    if not typ then
        ngx.say("failed to read: ", err)
        return
    end

    if typ == "header" then
        local key=res[1]
        local value=res[2]
        
        if stringy.startswith(string.lower(res[1]), "content-disposition") then
                local parts = stringy.split(res[3], ";")
                local current_parts = stringy.split(stringy.strip(parts[2]), "=")
                if string.lower(table.remove(current_parts, 1)) == "name" then
                    local current_value = stringy.strip(table.remove(current_parts, 1))
                    part_name = string.sub(current_value, 2, string.len(current_value) - 1)
                end
        end

        if key =="Content-Disposition" then
             local kvlist=string.split(value,';')

             for _, kv in ipairs(kvlist) do
                    local seg = string.trim(kv)
                    --ngx.log(ngx.ERR,seg)
                    if seg:find("filename") then
                        local kvfile = string.split(seg, "=")
                        filename = string.sub(kvfile[2], 2, -2)
                        if filename then
                            --获取文件后缀名字
                            fileExtension=getExtension(filename)
                            --ngx.log(ngx.ERR,fileExtension)
                            if extExists(fileExtension) == false then
                               msg['msg'] = '文件类型错误'
                               ngx.say(cjson.encode(msg))
                               return 1
                            end
                            local linuxTime=tostring(os.time())
                            local filename = linuxTime..filename
                            filePath= "/tmp/attachment/image/"..filename
                            file,errmsg = io.open(filePath, "w+")
                            --存储的文件路径                    
                            --ngx.say("failed to open file ", filePath)
                            if not file then
                                ngx.say("failed to open file ", filePath .. errmsg)
                                return 1
                            end
                            table.insert(fileNames,filename)
                        else
                            return 1
                        end
                        --跳出循环
                        break 
                    end
             end
        end
    elseif typ == "body" then
        if part_name ~= nil and part_name ~="" and part_name ~='file' then
                part_value = res
        end
        if file then
                table.insert(fileRes,res)
                file:write(res)
                sha1:update(res)
        end

    elseif typ == "part_end" then
        if part_name ~= nil and part_name ~="" and part_name ~='file' then
           params[part_name] = part_value

           -- Reset fields for the next part
           part_value = nil
           part_name = nil
        end

        if file then
           local filename = fileNames[#fileNames]
           --ngx.log(ngx.ERR,filename)   
           mg:writeFile(filename,table.concat(fileRes),longitude,latitude)
           fileRes = nil
           file:close()
           file = nil
        end
        local sha1_sum = sha1:final()
        sha1:reset()
        --my_save_sha1_sum(sha1_sum)
    elseif typ == "eof" then
        msg['success'] = true
        msg['msg'] = '上传成功'
        msg['data']= fileNames

        mg:updateInfo(fileNames,params)
                mg:close()
        ngx.say(cjson.encode(msg))
        fileNames = nil
        break

    else
        -- do nothing
    end
end

搭建图片服务(一)

软件列表

  • nginx-1.12.0.tar.gz
  • LuaJIT-2.0.5.zip
  • lua-nginx-module-0.10.10.zip
  • ngx_devel_kit-0.3.0.zip
  • lua-resty-upload-0.10.zip
  • lua-resty-string-0.10.zip
  • lua-cjson-2.1.0.tar.gz
  • luarocks-2.4.3.tar.gz
  • lua-stringy-0.4-1.zip
  • gd-devel-2.0.35-11.el6.x86_64.rpm (centos 6.x)
  • gd-devel-2.0.35-26.el7.x86_64.rpm (centos 7.x)

安装步骤

安装luajit

  1. 解压 unzip LuaJIT-2.0.5.zip
  2. 修改Makefile export PREFIX= /usr/local/luajit
  3. 编译安装 make && make install
  4. 配置环境变量
   vi /etc/profile
   export LUAJIT_LIB=/usr/local/luajit/lib
   export LUAJIT_INC=/usr/local/luajit/include/luajit-2.0 
   source /etc/profile

nginx安装

  1. 创建用户
useradd -M -s /sbin/nologin nginx

  1. 安装依赖
yum install gcc pcre pcre-devel zlib zlib-devel openssl openssl-devel libxml2 libxml2-devel libxslt libxslt-devel gd-devel geoip geoip-devel -y

yum install gd fontconfig-devel freetype-devel libX11-devel libXpm-devel libjpeg-devel libpng-devel -y

rpm -ivh gd-devel-2.0.35-11.el6.x86_64.rpm

  1. 编译安装
./configure \
 --prefix=/usr/local/nginx \
 --user=nginx \
 --group=nginx \
 --with-pcre \
 --with-http_ssl_module \
 --with-http_v2_module \
 --with-http_realip_module \
 --with-http_addition_module \
 --with-http_sub_module \
 --with-http_dav_module \
 --with-http_flv_module \
 --with-http_mp4_module \
 --with-http_gunzip_module \
 --with-http_gzip_static_module \
 --with-http_random_index_module \
 --with-http_secure_link_module \
 --with-http_stub_status_module \
 --with-http_auth_request_module \
 --with-http_image_filter_module \
 --with-mail \
 --with-mail_ssl_module \
 --with-stream_ssl_module \
 --add-module=/root/ngx_devel_kit-0.3.0 \
 --add-module=/root/lua-nginx-module-0.10.10

 make && make install

  1. 启动测试

./nginx: error while loading shared libraries: libluajit-5.1.so.2: cannot open shared object file: No such file or directory

执行 ln -s /usr/local/lib/libluajit-5.1.so.2 /lib64/libluajit-5.1.so.2

配置lua

  1. 修改nginx.conf

server前面增加

 lua_package_path "/usr/local/luajit/lib/lua/?.lua;/usr/local/nginx/lua_lib/?.lua;;";
 lua_package_cpath "/usr/local/luajit/lib/lua/5.1/?.so;;";

http里面增加

location /hello {
    default_type text/html;
    content_by_lua_file lua_lib/hello.lua
}

创建hello.lua

mkdir /usr/local/nginx/lua_lib/
cd /usr/local/nginx/lua_lib/
touch hello.lua
vi hello.lua

ngx.say('hello lua!')

测试访问 http://127.0.0.1/hello

配置图片上传

  1. 安装lua-cjson-2.1.0.tar.gz
  • tar -zxf lua-cjson-2.1.0.tar.gz
  • cd lua-cjson-2.1.0
  • 修改Makefile PREFIX = /usr/local/luajit
  • 修改Makefile LUA_INCLUDE_DIR = $(PREFIX)/include/luajit-2.0
  • make && make install
  1. 安装lua-resty-string-0.10.zip
  • unzip lua-resty-string-0.10.zip
  • cd lua-resty-string-0.10
  • 修改Makefile PREFIX ?= /usr/local/luajit
  • 修改Makefile LUA_INCLUDE_DIR ?= $(PREFIX)/include/luajit-2.0
  • make install
  1. 安装lua-resty-upload-0.10.zip
  • unzip lua-resty-upload-0.10.zip
  • cd lua-resty-upload-0.10
  • 修改Makefile PREFIX ?= /usr/local/luajit
  • 修改Makefile LUA_INCLUDE_DIR ?= $(PREFIX)/include/luajit-2.0
  • make install

4.安装luarocks

  • tar -zxf luarocks-2.4.3.tar.gz
  • cd luarocks-2.4.3
  • ./configure --with-lua-include=/usr/local/luajit/include/luajit-2.0 --prefix=/usr/local/luajit
  • make && make install
  • 将/usr/local/luajit/bin加入path
  1. 安装lua-stringy-0.4-1.zip
  • unzip lua-stringy-0.4-1.zip
  • cd lua-stringy-0.4-1
  • ln -s /usr/local/luajit/share/lua/5.1/luarocks /usr/share/lua/5.1/luarocks
  • luarocks make stringy/stringy-0.4-1.rockspec

上传nginx_upload.lua脚本

  1. 脚本内容
string.split = function(line, sep)

    sep = sep or ' '
    local retval = {}
    local pos = 1
    while true do
        local from, to = string.find(line, sep, pos)
        if from then
            local item = string.sub(line, pos, from-1)
            table.insert( retval, item )
            pos = to + 1
        else
            local item = string.sub(line, pos)
            table.insert( retval, item )
            break
        end
    end
    return retval
end    

string.trim = function (s)
  return (s:gsub("^%s*(.-)%s*$", "%1"))
end

--获取扩展名  
function getExtension(filename)  
    return filename:match(".+%.(%w+)$")  
end

local stringy = require "stringy"
local resty_sha1 = require "resty.sha1"
local upload = require "resty.upload"
local cjson = require "cjson"
local chunk_size = 4096
local form = upload:new(chunk_size)
local sha1 = resty_sha1:new()
local file
local extnames={"png","jpg","jpeg","gif"}
local msg = {success=false,msg='上传失败',data=nil}


function extExists(ext)
    for _, value in pairs(extnames) do
        if value == ext then
            return true
        end
    end

    return false
end

local fileNames={}
local fileRes={}
local part_name, part_value
local params={}
while true do
    local typ, res, err = form:read()

    if not typ then
        ngx.say("failed to read: ", err)
        return
    end

    if typ == "header" then
        local key=res[1]
        local value=res[2]
        
        if stringy.startswith(string.lower(res[1]), "content-disposition") then
                local parts = stringy.split(res[3], ";")
                local current_parts = stringy.split(stringy.strip(parts[2]), "=")
                if string.lower(table.remove(current_parts, 1)) == "name" then
                    local current_value = stringy.strip(table.remove(current_parts, 1))
                    part_name = string.sub(current_value, 2, string.len(current_value) - 1)
                end
        end

        if key =="Content-Disposition" then
             local kvlist=string.split(value,';')

             for _, kv in ipairs(kvlist) do
                    local seg = string.trim(kv)
                    --ngx.log(ngx.ERR,seg)
                    if seg:find("filename") then
                        local kvfile = string.split(seg, "=")
                        filename = string.sub(kvfile[2], 2, -2)
                        if filename then
                            --获取文件后缀名字
                            fileExtension=getExtension(filename)
                            --ngx.log(ngx.ERR,fileExtension)
                            if extExists(fileExtension) == false then
                               msg['msg'] = '文件类型错误'
                               ngx.say(cjson.encode(msg))
                               return 1
                            end
                            local linuxTime=tostring(os.time())
                            local filename = linuxTime..filename
                            filePath= "/tmp/attachment/image/"..filename
                            file,errmsg = io.open(filePath, "w+")
                            --存储的文件路径                    
                            --ngx.say("failed to open file ", filePath)
                            if not file then
                                ngx.say("failed to open file ", filePath .. errmsg)
                                return 1
                            end
                            table.insert(fileNames,filename)
                        else
                            return 1
                        end
                        --跳出循环
                        break 
                    end
             end
        end
    elseif typ == "body" then
        if part_name ~= nil and part_name ~="" and part_name ~='file' then
          part_value = res
        end
        if file then
                table.insert(fileRes,res)
                file:write(res)
                sha1:update(res)
        end

    elseif typ == "part_end" then
        if part_name ~= nil and part_name ~="" and part_name ~='file' then
           params[part_name] = part_value

           -- Reset fields for the next part
           part_value = nil
           part_name = nil
        end

        if file then
           local filename = fileNames[#fileNames]
           --ngx.log(ngx.ERR,filename)   
           fileRes = nil
           file:close()
           file = nil
        end
        local sha1_sum = sha1:final()
        sha1:reset()
        --my_save_sha1_sum(sha1_sum)
    elseif typ == "eof" then
        msg['success'] = true
        msg['msg'] = '上传成功'
        msg['data']= fileNames

        ngx.say(cjson.encode(msg))
        fileNames = nil
        break

    else
        -- do nothing
    end
end

  1. 创建图片目录
mkdir -p /tmp/attachment/image
chmod -R 777 /tmp/attachment/image

  1. 配置nginx.conf
location /upload {
    error_log logs/upload_err.log;
    content_by_lua_file lua_lib/nginx_upload.lua;
}

  1. 使用POSTMAN测试

配置裁剪缩略图

  1. 安装依赖
yum install ImageMagick

  1. 上传imagemagick.lua脚本
-- http://domain.com/111/photo/201411/05/5459e306820af926411357_320x320.jpg
-- config
local image_sizes = {"640x640", "320x320", "124x124", "140x140", "64x64", "60x60", "32x32", "0x0"}


-- parse uri
function parseUri(uri)
    local _, _, name, ext, size = string.find(uri, "(.+)(%..+)!(%d+x%d+)")
    --ngx.header.content_type = "text/plain";
    --ngx.say(name,size);
    if name and size and ext then
        return ngx.var.image_root .. name .. ext, size
    else
        return "", ""
    end
end

function fileExists(name)
    local f = io.open(name, "r")
    if f ~= nil then
        io.close(f)
        return true
    else
        return false
    end
end
--ngx.header.content_type = "text/plain";
--ngx.say(name);
function sizeExists(size)
    for _, value in pairs(image_sizes) do
        if value == size then
            return true
        end
    end
    
    return false
end
--ngx.header.content_type = "text/plain";
--ngx.say(size);
function resize()
    local ori_filename, size = parseUri(ngx.var.uri)
    --ngx.header.content_type = "text/plain";
    ngx.log(ngx.ERR,ori_filename..'!'..size);
    if fileExists(ori_filename) == false then  --or sizeExists(size) == false then
        ngx.exit(404)
    end
    --local i = string.find(ori_filename,'.',1,true)
    --local name = string.sub(ori_filename,0,i-1)
    --local ext = string.sub(ori_filename,i,string.len(ori_filename))
    --local dist_file_name = name..size..ext
    --ngx.log(ngx.ERR,'-----'..dist_file_name..'------------') 
    local command = '';
    if size == '0x0' then
        command = table.concat({
            ngx.var.convert_bin,
            ori_filename,
            "/usr/local/nginx/conf/logo.png",
            "-gravity southeast -geometry +5+10 -composite",
            ngx.var.file,
        }, " ")
    else
        command = table.concat({
            ngx.var.convert_bin,
            ori_filename,
            "/usr/local/nginx/conf/logo.png",
            "-gravity southeast -geometry +5+10 -composite -resize",
            size,
            ngx.var.file,
        }, " ")
    end
    --ngx.header.content_type = "text/plain";
    --ngx.say(command);
    os.execute(command)
end
--ngx.header.content_type = "text/plain";
--ngx.say(command);
resize()

  1. 配置nginx.conf
rewrite ^/attachment/image/(.*)\.(png|jpg|jpeg|gif)$ /attachment/image/$1.$2!0x0 last;
location /attachment/image/ {
    root /tmp;
    set $image_root "/tmp";
    set $file "$image_root$uri";
    set $convert_bin "/usr/bin/convert";
    if (!-f $file){
       rewrite_by_lua_file lua_lib/imagemagick.lua;
    }
    expires max;
}
  1. 上传水印图片 /usr/local/nginx/conf/logo.png

  2. 重新加载配置 nginx -s reload

  3. 测试

    http://127.0.0.1/attachment/image/aaaa.png!128x128