一.应用背景
在大多数网站中都会用到图片进行裁剪处理,特别是电商网站都会必须都用到图片进行裁剪,在不同位置都会有不同尺寸进行展示。
在通常情况下大多数采用上传后,程序把图片进行裁剪不同的缩略图。这样在单点得站点是没有什么问题,当访问量大了,服务器从一台变为多台时,问题就很明显了,很不利于扩展。
我们可以采用以下几种方式去解决:
- 可以使用七牛、又拍云提供的云存储及数据处理服务,解决图片的处理、存储、多节点访问速度的问题,这种方式优点是方案成熟,相应的有一定费用和开发工作,另外有一些小概率的风险,比如云服务挂掉影响本站访问。
- 使用第三方的图片处理程序,比如zimg,点击查看使用手册,@招牌疯子开发。zimg的性能和扩展性不错,文档也很完善,会继续保持关注。
- 自己撸出一套代码出来
我选择第三种方式,说起openresty是无意中发现的,发现后一发不可收拾的爱上了,图片上传删除缩略全部交由openresty配合Lua单独模块出来处理,现在来说说缩略模块的实现模式,后续可以通过GraphicsMagick来实现更多功能。
二.相关规范
- 链接地址
原图访问地址:http://blog.linsongzheng.com/5/4/63c299422dfb1b80a6ba6502db5b1254.jpg
缩略图访问地址:http://blog.linsongzheng.com/5/4/63c299422dfb1b80a6ba6502db5b1254.jpg_100x100.jpg(不同尺寸将由后面的100×100来控制,意思就是width x height) - 访问流程
先判断缩略图文件是否存在,如果存在直接显示缩略图
如不存在则执行以下流程- 判断缩略图链接与规则是否匹配,如不匹配,则404退出;如匹配跳至2
- 判断原图是否存在,如原图存在则跳至3,如不存在则进入下一步;
- 拼接Graphicsmagick命令,生成并显示缩略图
三.相关代码配置
- openresty的配置
server{ server_name blog.linsongzheng.com; listen 80; index index.html; root /var/www/images; location ~* ^(.+\.(jpg|jpeg|gif|png))_(\d+)x(\d+)(c|t|b)\.(jpg|jpeg|png|gif)$ { set $origin_file $document_root$1; set $width $3; set $height $4; set $flag $5; set $ext $6; if (!-f $origin_file) { return 404; } if (!-f $request_filename) { access_by_lua_file lua/thumbnail.lua; } } }
- Lua代码
-- nginx thumbnail module -- last update : 2015/11/26 -- version : 0.0.1 --[[ uri :链接地址,如/5/4/63c299422dfb1b80a6ba6502db5b1254.jpg_328x328.jpg img_width :缩略图宽度 img_width :缩略图高度 gm_path :gm命令路径 flag :标记 ]] local uri = ngx.var.uri local img_width = ngx.var.width local img_height = ngx.var.height or img_width local cur_uri_reg = '_[0-9]+x[0-9]+' local gm_path = '/usr/bin/gm' local flag = ngx.var.flag local left,top,rw,rh,r = 0,0,img_width,img_height,img_width/img_height local p = io.popen(gm_path .. ' identify ' .. ngx.var.origin_file) local info = p:read('*all') p:close() local types = {['jpg'] = 'image/jpeg', ['jpeg'] = 'image/jpeg', ['png'] = 'image/png'} if info then _,_,origin_width,origin_height = string.find(info, cur_uri_reg) origin_width,origin_height = tonumber(origin_width),tonumber(origin_height) if r > origin_width / origin_height then rh = math.ceil((origin_height / origin_width) * rw) top = math.ceil((rh - img_height) / 2) else rw = math.ceil((origin_width / origin_height) * rh) left = math.ceil((rw - img_width) / 2) end end cmd = gm_path .. ' convert -quality 95 ' .. ngx.var.origin_file .. " -resize \"" .. rw .. 'x' .. rh .. ">\" +profile \"*\"" if flag == 't' then top = 0 cmd = cmd .. " -crop " .. img_width .. 'x' .. img_height .. "+" .. left .. "+0 " elseif flag == 'b' then top = rh - img_height cmd = cmd .. " -crop " .. img_width .. 'x' .. img_height .. "+" .. left .. "+" .. top .. " " else cmd = cmd .. " -gravity center -extent " .. img_width .. "x" .. img_height .. " " end cmd = cmd .. ngx.var.request_filename os.execute(cmd) ngx.header.content_type = types[ngx.var.ext] local f = io.open(ngx.var.request_filename) ngx.print(f:read('*a')) f:close() ngx.flush() return ngx.eof()