Posted on 18 , 九月 2012 in 未分类 author: Syang
tenginx+lua+shell(conver)+其实也是nginx+lua,因为项目的需求变化,包括界面改版的变化,以至于每一版本的图片尺寸不定,所以不可能保存不同尺寸的版本。所以只能在线根据需求,由服务器来自动处理。
第一版我是使用了nginx+phpfpm,实现起来非常easy,但是稳定性方面不佳,
第二版换成了lua,效果还不错,由于lua本身没有对图片处理的模块(可能有第三方的,不过我还没有深入研究),所以lua是调用shell脚本实现的(当然在调用shell脚本时,程序会blocking,性能有些影响),稳定性增加。>
首先,我们定了一些生成的规则, 生成参数,是放在主文件名的后面
例如,http://pic.yiibook.com/example.jpg这个是源图片
那么在调用缩略图时,使用http://pic.yiibook.com/example!200×100.jpg 就会生成一张以源图为基础的200宽100高的图片 !200×100就是具体的参数
ok,下面说一下我定义的参数,有几种情况,具体是根据业务上面的需求,而自行调用的
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