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