需求
在文章模块,上传的封面图片在不同的场景使用,可以任意的剪切不同大小的图片以供使用。类似xx.png_100x100.png的任意尺寸的图片。
本文通过nginx + lua + imageMagic的技术方案实现图片的剪切功能。在实现此功能的时候我们需要对nginx、lua、以及imageMagic有一个大致的了解。下面先分别介绍相关的知识,最后通过代码完成整个功能的搭建。
nginx基本知识
location指令的作用是根据用户请求的URI来执行不同的应用,也就是根据用户请求的网站URL进行匹配,匹配成功即进行相关的操作。
location的语法
location [ = | ~ | ~& | ^~] uri {...}
指令 || 匹配表示 || 匹配的网站网址 || 匹配uri后要执行的配置
=
为精准匹配
~
区分大小写
~*
不区分大小写
^~
表示不做正则表达式的检查。
常见采用~*
不区分大小写的正则来匹配uri做一些配置。
正则基本知识
详细的正则知识查看这里 http://tool.oschina.net/uploads/apidocs/jquery/regexp.html
下面主要说常用到的几个表达式
\ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符。例如,“n”匹配字符“n”。“\n”匹配一个换行符。串行“\\”匹配“\”而“\(”则匹配“(”。
^ 开始
$ 结束
* 匹配前面的子表达式零次或多次
+ 匹配前面的子表达式一次或多次
? 匹配前面的子表达式零次或一次
. 匹配除”\n“ 外的任何单个字符
\d 匹配一个数字
.+ 除\n 外的任意子表达式一次或多次
lua基本语法
先大致的了解下lua。内容摘自 http://www.runoob.com/lua/lua-tutorial.html
Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
Lua 特性
轻量级: 它用标准C语言编写并以源代码形式开放,编译后仅仅一百余K,可以很方便的嵌入别的程序里。
*可扩展: *Lua提供了非常易于使用的扩展接口和机制:由宿主语言(通常是C或C++)提供这些功能,Lua可以使用它们,就像是本来就内置的功能一样。
其它特性:
- 支持面向过程(procedure-oriented)编程和函数式编程(functional programming);
- 自动内存管理;只提供了一种通用类型的表(table),用它可以实现数组,哈希表,集合,对象;
- 语言内置模式匹配;闭包(closure);函数也可以看做一个值;提供多线程(协同进程,并非操作系统所支持的线程)支持;
- 通过闭包和table可以很方便地支持面向对象编程所需要的一些关键机制,比如数据抽象,虚函数,继承和重载等。
a = 5 -- 全局变量
local b = 5 -- 局部变量
~= --表示非运算
-- 流程控制
if(boolean_expression)
then
--[ statement(s) will execute if the boolean expression is true --]
else if( boolean_expression 2)
--[ Executes when the boolean expression 2 is true --]
else
--[ executes when the none of the above condition is true --]
end
-- 嵌套
if( 布尔表达式 1)
then
--[ 布尔表达式 1 为 true 时执行该语句块 --]
if(布尔表达式 2)
then
--[ 布尔表达式 2 为 true 时执行该语句块 --]
end
end
-- 使用 .. 进行字符串连接
print("连接字符串",string1..string2..string3)
在大致了解了字符串、变量、流程控制后,我们就可以简单的实现lua中的代码了。
nginx、lua、imageMagic 安装
nginx 、lua、和imageMagic的安装可参照这篇文章。
代码实现细节
nginx中重点就是匹配url,当符合规则的url时进行图片生成和剪裁。
首先看我们要实现的图片规则,xxx.jpg_200x200f_q80.jpg.webp,我们需要通过url规则生成不同的图片尺寸和大小。
nginx中代码示例
location ~* ^(.+\.(jpg|jpeg|gif|png))_(\d+)x(\d+)(f?)\.(jpg|jpeg|gif|png)(\.webp)?$ {
if (!-f $request_filename) {
access_log /usr/local/nginx/logs/ImageResizer.log main;
add_header X-Powered-By 'Lua GraphicsMagick'; # 此 HTTP Header 无实际意义,用于测试
add_header file-path $request_filename; # 此 HTTP Header 无实际意义,用于测试
#lua_code_cache off; # 在编写外部 Lua 脚本时,设置为 off Nginx 不会缓存 Lua,方便调试
set $request_filepath /home/leewr/mono/app$1; # 设置原始图片路径,如:/document_root/1.gif
set $width $3; # 设置裁剪/缩放的宽度
set $height $4; # 设置裁剪/缩放的高度
set $enforce $5; # 是否强制裁剪
set $ext $6; # 图片文件格式后缀
set $webp $7; # 支持webp返回webp图片格式图片
#content_by_lua_block {
#ngx.say(ngx.var.noresize)
#}
content_by_lua_file lua/ImageResizer.lua;
}
}
这里的正则我们分组匹配了以.+
开始的任意字符然后是jpg,再是_
\d+
x\d+
(f?)
零次或一次最后是jpg|jeg|gif|png
结尾,最后是webp。
通过正则的匹配我们拿到$1, $2, $3
等值并赋值给$width\$height
等变量,最后在lua图片转换中调用,执行相关图片转换命令。
lua中代码简单实现
local sizeType
local command
if ngx.var.enforce == 'f'
then
command = "convert "..ngx.var.request_filepath.." -resize "..ngx.var.width.."x"..ngx.var.height.."^ -gravity center -extent ".. ngx.var.width .. "x" .. ngx.var.height .." "..ngx.var.request_filepath.."_"..ngx.var.width.."x"..ngx.var.height..ngx.var.enforce.."."..ngx.var.ext
sizeType = "f"
else
command = "convert " ..ngx.var.request_filepath.." -resize ".. ngx.var.width .. "x" .. ngx.var.height .. " +profile \"*\" " .. ngx.var.request_filepath .. "_" .. ngx.var.width .. "x" .. ngx.var.height .. ngx.var.enforce .. "." .. ngx.var.ext
sizeType = ""
end
-- local command = "convert " ..ngx.var.request_filepath.." -resize ".. ngx.var.width .. "x" .. ngx.var.height .. ngx.var.enforce .. " +profile \"*\" " .. ngx.var.request_filepath .. "_" .. ngx.var.width .. "x" .. ngx.var.height .. ngx.var.enforce .. "." .. ngx.var.ext
if ngx.var.webp == ".webp"
then
command = command ..ngx.var.webp
end
-- ngx.say(ngx.var.noresize)
os.execute(command)
ngx.exec(ngx.var.request_uri)
过程不复杂,当enforce为强制转换的时候设置command命令,当有webp格式转换的时候生成webp图片。最后调用os.execute()执行imageMagic命令进行转换。最后返回请求的图片地址,此时图片已经生成,图片正确显示。
总结
通过了nginx层我们实现了一个图片资源的尺寸转换裁剪功能。在往更深层一面思考,是否可以做更多?将资源的管理统一起来,形成一个独立的资源管理服务。所有的图片操作都采用统一的管理。
网友评论