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

相關文章
相關標籤/搜索