tengine+lua实现时时在线图片缩放,剪切。

tengine+lua实现时时在线图片缩放,剪切。php

Posted on 18 , 九月 2012 in 未分类 author: Syangcss

tenginx+lua+shell(conver)+其实也是nginx+lua,由于项目的需求变化,包括界面改版的变化,以致于每一版本的图片尺寸不定,因此不可能保存不一样尺寸的版本。因此只能在线根据需求,由服务器来自动处理。html

初版我是使用了nginx+phpfpm,实现起来很是easy,可是稳定性方面不佳,nginx

第二版换成了lua,效果还不错,因为lua自己没有对图片处理的模块(可能有第三方的,不过我尚未深刻研究),因此lua是调用shell脚本实现的(固然在调用shell脚本时,程序会blocking,性能有些影响),稳定性增长。>shell

首先,咱们定了一些生成的规则, 生成参数,是放在主文件名的后面编程

例如,http://pic.yiibook.com/example.jpg这个是源图片服务器

那么在调用缩略图时,使用http://pic.yiibook.com/example!200×100.jpg 就会生成一张以源图为基础的200宽100高的图片 !200×100就是具体的参数yii

ok,下面说一下我定义的参数,有几种状况,具体是根据业务上面的需求,而自行调用的ide

1.固定尺寸缩放性能

!200×100 将源图缩放为宽200x高100

!200 将源图缩放为宽200x高200

!200×100-50 将源图缩放为宽200x高100 而且图片质量为50 (这个是为了给手机端使用的,由于手机端可能须要图片的size更小一些

!200-50 将源图缩放为宽200x高200 而且图片质量为50

这个参数会将源图强制缩放到这个尺寸,因此可能会有所变形

2.等比缩放

:w200 将源图以宽为准200,开始缩放,(意思是,强制将源图的宽缩到200,高无论,那么这个图是一个与源图比例相同的,不会变型

:h200 将源图以高为准200,开始缩放, 意思与上面相似

:m200 将源图以(宽,高那个值大,以哪一个为准,进行缩放,好比源图是300×400,那就会以高为准,先将高缩到200),可是若是宽高都没有达到,而不处理

同时也支持 :w200-50 :h200-50 :m200-50 的图片质量

3.中心剪辑

@200×300 将源图以(宽,高那个值小,以哪一个为准,进行缩放,并在缩放后的图片,以另外一边中间点(就是正中间,进行剪辑)

@200×300-50 同时支持图片质量

暂时个人程序就支持这三种参数格式,

OK,下面看一下nginx配置

    server
    {
           listen       80;

           server_name  pic.yiibook.com;

           #access_log  logs/pic.access.log  main;

           root   /var/www/pic;

           location / {
               index index.html;
           }

           #宽,高 像素
           location ~ (.*)!(\d+)x(\d+).(gif|jpg|jpeg|png)$  {
               root   /var/file/thumb/picture;

               #bucketname = picture
               set $bucketname picture;
               #原图片路径
               set $srcPath /var/file/$bucketname;
               #目录图片路径
               set $destPath /var/file/thumb/$bucketname;

               #处理类型
               set $type 1;

               if (!-f $request_filename) {
                   rewrite_by_lua_file conf/lua/picture_image_thumb.lua;
               }
               expires 30d;
           }

           #宽,高 像素 质量
           location ~ (.*)!(\d+)x(\d+)-(\d+).(gif|jpg|jpeg|png)$  {
               root   /var/file/thumb/picture;

               #bucketname = picture
               set $bucketname picture;
               #原图片路径
               set $srcPath /var/file/$bucketname;
               #目录图片路径
               set $destPath /var/file/thumb/$bucketname;

               #处理类型
               set $type 2;

               if (!-f $request_filename) {
                   rewrite_by_lua_file conf/lua/picture_image_thumb.lua;
               }
               expires 30d;
           }

           #宽高相等
           location ~ (.*)!(\d+).(gif|jpg|jpeg|png)$  {
               root   /var/file/thumb/picture;

               #bucketname = picture
               set $bucketname picture;
               #原图片路径
               set $srcPath /var/file/$bucketname;
               #目录图片路径
               set $destPath /var/file/thumb/$bucketname;

               #处理类型
               set $type 3;

               if (!-f $request_filename) {
                   rewrite_by_lua_file conf/lua/picture_image_thumb.lua;
               }
               expires 30d;
           }
                      #宽高相等质量
           location ~ (.*)!(\d+)-(\d+).(gif|jpg|jpeg|png)$  {
               root   /var/file/thumb/picture;

               #bucketname = picture
               set $bucketname picture;
               #原图片路径
               set $srcPath /var/file/$bucketname;
               #目录图片路径
               set $destPath /var/file/thumb/$bucketname;

               #处理类型
               set $type 4;

               if (!-f $request_filename) {
                   rewrite_by_lua_file conf/lua/picture_image_thumb.lua;
               }
               expires 30d;
           }

           #宽,高 像素等比
           location ~ (.*):(w|h|m)(\d+).(gif|jpg|jpeg|png)$  {
               root   /var/file/thumb/picture;

               #bucketname = picture
               set $bucketname picture;
               #原图片路径
               set $srcPath /var/file/$bucketname;
               #目录图片路径
               set $destPath /var/file/thumb/$bucketname;

               #处理类型
               set $type 5;

               if (!-f $request_filename) {
                   rewrite_by_lua_file conf/lua/picture_image_thumb.lua;
               }
               expires 30d;
           }
           
           #宽,高 像素等比 质量
           location ~ (.*):(w|h|m)(\d+)-(\d+).(gif|jpg|jpeg|png)$  {
               root   /var/file/thumb/picture;

               #bucketname = picture
               set $bucketname picture;
               #原图片路径
               set $srcPath /var/file/$bucketname;
               #目录图片路径
               set $destPath /var/file/thumb/$bucketname;

               #处理类型
               set $type 6;

               if (!-f $request_filename) {
                   rewrite_by_lua_file conf/lua/picture_image_thumb.lua;
               }

               expires 30d;
           }

           #宽,高 CUT
           location ~ (.*)\@(\d+)x(\d+).(gif|jpg|jpeg|png)$  {
               root   /var/file/thumb/picture;

               #bucketname = picture
               set $bucketname picture;
               #原图片路径
               set $srcPath /var/file/$bucketname;
               #目录图片路径
               set $destPath /var/file/thumb/$bucketname;

               #处理类型
               set $type 7;

               if (!-f $request_filename) {
                   rewrite_by_lua_file conf/lua/picture_image_thumb.lua;
               }

               expires 30d;
           }
           
           #宽,高 CUT 质量
           location ~ (.*)\@(\d+)x(\d+)-(\d+).(gif|jpg|jpeg|png)$  {
               root   /var/file/thumb/picture;

               #bucketname = picture
               set $bucketname picture;
               #原图片路径
               set $srcPath /var/file/$bucketname;
               #目录图片路径
               set $destPath /var/file/thumb/$bucketname;

               #处理类型
               set $type 8;

               if (!-f $request_filename) {
                   rewrite_by_lua_file conf/lua/picture_image_thumb.lua;
               }

               expires 30d;
           }

           location ~ .*.(gif|jpg|jpeg|png|bmp|swf|css|js|html)$ {
               expires 30d;
           }

           error_page   500 502 503 504  /50x.html;
           location = /50x.html {
               root   html;
           }
     }

配置文件有些长,主要是匹配参数的规制,若是缩略图存在,就不生成了,大约有8个规则匹配,上面的set $type 8会传给lua进行处理

若是你是初次进行nginx+lua编程,建议使用file,如rewrite_by_lua_file,不要直接在nginx的配置文件中写lua代码,由于单引,双引的问题,会出现很诡异的问题

下面咱们再看一下picture_image_thumb.lua的代码

local wh = {
        {'w',170},
        {'w',200},
        {'w',224},
        {'w',365},
        {'w',150},
        {'w',237},
        {'w',420},
        {'w',450},
        {'h',32},
        {'m',600},
}

-- 定义可缩放尺寸
local xy = {
        {132,123},
        {404,250},
        {239,192},
        {415,353},
        {157,124},

        {210,131},
        {150,100},
        {110,80},
        {200,150},

        {110,110},
        {345,230},
        {164,105},
        {231,73},
}

local qualitys = {10,20,30,40,50,60,70,80,90,100}

-- 匹配模式
-- 宽, 高,
if ngx.var.type == string.format('%d', 1) and string.find(ngx.var.uri, '(.*)!(%d+)x(%d+)%.(%w+)') then
        _, _, uriName, width, height, extName = string.find(ngx.var.uri, '(.*)!(%d+)x(%d+)%.(%w+)')

        size = "!" .. width .. "x" .. height
        srcFile = ngx.var.srcPath .. uriName .. "." .. extName

-- 宽, 高, 质量
elseif ngx.var.type == string.format('%d', 2)  and string.find(ngx.var.uri, '(.*)!(%d+)x(%d+)-(%d+)%.(%w+)') then
        _, _, uriName, width, height, quality, extName = string.find(ngx.var.uri, '(.*)!(%d+)x(%d+)-(%d+)%.(%w+)')

        size = "!" .. width .. "x" .. height .. '-' .. quality
        srcFile = ngx.var.srcPath .. uriName .. "." .. extName

-- 宽高相等
elseif ngx.var.type == string.format('%d', 3)  and string.find(ngx.var.uri, '(.*)!(%d+)%.(%w+)') then
        _, _, uriName, width, extName = string.find(ngx.var.uri, '(.*)!(%d+)%.(%w+)')
        size = "!" .. width
        height = width
        srcFile = ngx.var.srcPath .. uriName .. "." .. extName

-- 宽高相等, 质量
elseif ngx.var.type == string.format('%d', 4)  and string.find(ngx.var.uri, '(.*)!(%d+)-(%d+)%.(%w+)') then
        _, _, uriName, width, quality, extName = string.find(ngx.var.uri, '(.*)!(%d+)-(%d+)%.(%w+)')

        size = "!" .. width .. '-' .. quality
        height = width
        srcFile = ngx.var.srcPath .. uriName .. "." .. extName

-- 宽,高 等比
elseif ngx.var.type == string.format('%d', 5)  and string.find(ngx.var.uri, '(.*):([whm])(%d+)%.(%w+)') then
        _, _, uriName, sidetype, num, extName = string.find(ngx.var.uri, '(.*):([whm])(%d+)%.(%w+)')

        size = ":" .. sidetype .. num
        srcFile = ngx.var.srcPath .. uriName .. "." .. extName

-- 宽,高 等比, 质量
elseif ngx.var.type == string.format('%d', 6)  and string.find(ngx.var.uri, '(.*):([whm])(%d+)-(%d+).(%w+)') then
        _, _, uriName, sidetype, num, quality, extName = string.find(ngx.var.uri, '(.*):([whm])(%d+)-(%d+)%.(%w+)')
        size = ":" .. sidetype .. num ..  '-' .. quality
        srcFile = ngx.var.srcPath .. uriName .. "." .. extName

-- 宽,高 CUT
elseif ngx.var.type == string.format('%d', 7)  and string.find(ngx.var.uri, '(.*)@(%d+)x(%d+)%.(%w+)') then
        _, _, uriName, width, height, extName = string.find(ngx.var.uri, '(.*)@(%d+)x(%d+)%.(%w+)')
        size = "@" .. width .. "x" .. height
        srcFile = ngx.var.srcPath .. uriName .. "." .. extName

elseif ngx.var.type == string.format('%d', 8)  and string.find(ngx.var.uri, '(.*)@(%d+)x(%d+)-(%d+)%.(%w+)') then
        _, _, uriName, width, height, quality, extName = string.find(ngx.var.uri, '(.*)@(%d+)x(%d+)-(%d+)%.(%w+)')
        size = "@" .. width .. "x" .. height .. '-' .. quality
        srcFile = ngx.var.srcPath .. uriName .. "." .. extName
end

if quality ~= nil then
        -- 检测图片质量是否在范围内
        local qualityfound = 0
        for i=1, #qualitys do
                local q = qualitys[i]
                if string.format('%d', q) == string.format('%d', quality) then
                        qualityfound = 1
                        break;
                end
        end

        if  qualityfound == 0 then
                ngx.req.set_uri(ngx.var.uri, true)
        end
end

-- 等比
if sidetype ~= nil then
        local sidetypefound=0
        local i = 1
        for i=1, #wh do
                local t = wh[i][1]
                local n = wh[i][2]
                if string.format('%s', t) == sidetype and string.format('%d', n) == num then
                        sidetypefound = 1
                        break;
                end
        end

        if  sidetypefound == 0 then
                ngx.req.set_uri(ngx.var.uri, true)
        end
else


        local found=0
        local i = 1
        for i=1, #xy do
                local x = xy[i][1]
                local y = xy[i][2]
                if string.format('%d', x) == width and string.format('%d', y) == height then
                        found = 1
                        break;
                end
        end

        if found == 0 then
        -- 显示文件
        ngx.req.set_uri(ngx.var.uri, true)
    end
end

local i = 0
local last = 0
while true do
    i = string.find(uriName,'/',i+1)
    if i == nil then break end
    last = i
end

-- 文件路径
local uriPath = string.sub(uriName, 1, last)
-- 主文件名
local mainFilename = string.sub(uriName, last+1)

-- 建立目录
local dircmd = '/bin/mkdir -p ' .. ngx.var.destPath .. uriPath
os.execute(dircmd)

-- 建立文件
local command = '/opt/scripts/makeImage.sh \\' .. size .. ' ' .. srcFile .. ' ' .. ngx.var.destPath .. uriName .. '\\' .. size .. '.' .. extName
os.execute(command)

-- 显示文件
ngx.req.set_uri(ngx.var.uri, true)

lua文件主要是进行参数的验证,具体处理图片,都交给了shell脚本,由于若是不限制宽度,那么会被攻击,产生没有必要的图片,浪费服务器资源。

下面咱们看一下makeImage.sh脚本,脚本调用了conver和identify命令,请自行安装imagemagick

#/bin/sh
CONVERT=/usr/bin/convert
IDENTIFY=/usr/bin/identify
DEFAULTQUALITY=70

make1 () {
        T=`echo "$1" | cut -b2- | cut -d- -f1`
        Q=`echo "$1" | cut -b2- | cut -d- -f2`
        if [ "$Q" == "$T" ]; then
                Q=$DEFAULTQUALITY
        fi

        W=`echo "$T" | cut -dx -f1`
        H=`echo "$T" | cut -dx -f2`
        $CONVERT $2 -resize $W\!x$H! -quality $Q $3
}

make2 () {
        #是宽仍是高或才是m
        C=`echo "$1" | cut -b2`
        #像素
        T=`echo "$1" | cut -b3- | cut -d- -f1`
        #质量
        Q=`echo "$1" | cut -b3- | cut -d- -f2`
        if [ "$Q" == "$T" ]; then
                Q=$DEFAULTQUALITY;
        fi
        case "$C" in
                w)
                        RESIZE=$T
                        ;;
                h)
                        RESIZE=x$T
                        ;;
                m)
                        WH=`$IDENTIFY -format "%wx%h" $2`
                        W=`echo "$WH" | cut -dx -f1`
                        H=`echo "$WH" | cut -dx -f2`
                        if test `/usr/bin/expr $T - $W` -gt 0 && test `/usr/bin/expr $T - $H` ; then
                                RESIZE=$Wx$H
                        elif test `/usr/bin/expr $W - $H` -gt 0 ; then
                                RESIZE=$T
                        else
                                RESIZE=x$T
                        fi
                        ;;
                *)
                        exit 1;
        esac

        $CONVERT $2 -resize $RESIZE -quality $Q $3
}

make3 () {
        T=`echo "$1" | cut -b2- | cut -d- -f1`
        Q=`echo "$1" | cut -b2- | cut -d- -f2`
        if [ "$Q" == "$T" ]; then
                Q=$DEFAULTQUALITY
        fi

        W=`echo "$T" | cut -dx -f1`
        H=`echo "$T" | cut -dx -f2`

        SRCWH=`$IDENTIFY -format "%wx%h" $2`
        SRCW=`echo "$SRCWH" | cut -dx -f1`
        SRCH=`echo "$SRCWH" | cut -dx -f2`

        if test `/usr/bin/expr $SRCW - $SRCH` -gt 0 ; then
                FLAG='W'
                RESIZE=x$H
        else
                FLAG='H'
                RESIZE=$W
        fi

        TMPFILE=`mktemp`
        /bin/rm -rf $TMPFILE
        $CONVERT $2 -resize $RESIZE $TMPFILE
        REWH=`$IDENTIFY -format "%wx%h" $TMPFILE`
        REW=`echo "$REWH" | cut -dx -f1`
        REH=`echo "$REWH" | cut -dx -f2`
        if test $FLAG == 'W'; then
                DESTW=`/usr/bin/expr $W / 2`
                RESIZEW=`/usr/bin/expr $REW / 2`
                OFFSETW=`/usr/bin/expr $RESIZEW - $DESTW`
                $CONVERT -crop $T+$OFFSETW+0 -quality $Q $TMPFILE $3

        else
                DESTH=`/usr/bin/expr $H / 2`
                RESIZEH=`/usr/bin/expr $REH / 2`
                OFFSETH=`/usr/bin/expr $RESIZEH - $DESTH`
                $CONVERT -crop $T+0+$OFFSETH -quality $Q $TMPFILE $3
        fi
        /bin/rm -rf $TMPFILE
}

if test ! -f $2; then
        echo "没法访问$2: 没有那个文件"
        exit 1;
fi

$IDENTIFY -format "%wx%h" $2 > /dev/null 2>&1
REVAL=`echo $?`
if [ "x$REVAL" != "x0" ]; then
        echo "$2不是图片类型文件"
        exit 1;
fi

TYPE=`echo "$1" | cut -b1`;

case "$TYPE" in
  !)
        make1 $1 $2 $3
        ;;
  :)
        make2 $1 $2 $3
        ;;
  @)
        make3 $1 $2 $3
        ;;
  *)
        echo $"Usage: $0 {:w100|!200|@300x200}"
        exit 1
esac

如今这个程序就编写完了,若是你有任何问题,欢迎与我探讨,QQ:8025628