上一次我们已经配好了nginx服务器,使其能够运行lua脚本,同时能够用lua脚本进行js文件和css文件的合并,已经css文件的压缩。但是那个脚本并不能进行js合并后的文件的压缩。这也是我们需要解决的一个问题。由于作者我的lua脚本编写能力不是很高,只能看得懂部分的内容,不过这样就足够了,可以通过修改icombo.lua文件,来达到js文件压缩的目的。但是js是按照何种标准进行压缩的呢?于是我搜索到了一篇介绍压缩js的文章,同时它附带了一个c语言程序.可以给我们提供文件的压缩。这就是[jsMin],(http://www.crockford.com/javascript/jsmin.html)你可以点击这个链接去查看详细内容。好了话不多说,来开始添加新的功能吧。
配置安装前提:
1. 阅读 上一篇文章,服务器的基本配置。
2. 添加lua的posix模块(注:上期未介绍)
3. 下载jsMin脚本,下载地址
一:linux服务器下载jsMin脚本
wget https://github.com/douglascrockford/JSMin/archive/master.zip
unzip master
cd JSMIN-master
gcc -o jsmin jsmin.c
mv jsmin /usr/bin
二:验证jsmin是否具有压缩js功能
cd /opt/nginx/html/js
ll
-rw-r--r--. 1 root root 566808 Apr 15 16:01 backbone.js
-rw-r--r--. 1 root root 507676 Apr 15 16:00 underscore.js
jsmin < backbone.js > backbone.min.js
jsmin < backbone.js > underscore.min.js
这里需要强调几点知识。
- 管道流 < 表示input流
- 管道流 > 表示output流
- 两个文件名不能一直,否则会导致最后文件为空字节
我们查看一下压缩后文件的大小。
ll
-rw-r--r--. 1 root root 35186 Apr 17 19:19 backbone.js
-rw-r--r--. 1 root root 20012 Apr 17 19:20 backbone.min.js
-rw-r--r--. 1 root root 54539 Apr 17 19:20 underscore.js
-rw-r--r--. 1 root root 25662 Apr 17 19:21 underscore.min.js
大概压缩了一半左右的内容,之后我们可以去试一试这两个文件是否还能有效。
三: 修改上次说的icombo.lua文件
cd /opt/nginx/scripts/lua/icombo
vi icombo.lua
icombo.lua修改后的文件如下
-------------------------------
-- iCombo version 0.2.4 beta
-------------------------------
-------- explode string -------
function explode(s, delimiter)
result = {}
for match in (s..delimiter):gmatch("(.-)"..delimiter) do
table.insert(result, match)
end
return result
end
-------- get client ip --------
function getClientIp()
IP = ngx.req.get_headers()["X-Real-IP"]
if IP == nil then
IP = ngx.var.remote_addr
end
if IP == nil then
IP = "unknown"
end
return IP
end
-------- table find --------
function tableFind(t, e)
for _, v in ipairs(t) do
if v == e then
return true
end
end
return nil
end
-------- read file --------
function fileRead(filename)
local f = io.open(filename, "r")
if f == nil then
return
end
local t = f:read("*all")
f:close()
return t
end
-------- write file --------
function fileWrite(filename, content)
local f = io.open(filename, "w")
if f == nil then
return nil
end
local t = f:write(content)
f:close()
return true
end
-------- get file extension --------
function getFileExt(filename)
return filename:match(".+%.(%w+)$")
end
-------- check value empty --------
function empty(val)
if val == nil or val == '' then
return true
end
return
end
-------- remove css comments ---------
function removeCssComents(out)
out = out:gsub("/%*.-%*/", "")
:gsub('\n%s', "")
:gsub('^%s*', "")
:gsub('\n$', "")
:gsub('%s*{', "{")
:gsub(';%s*', ";")
:gsub(';\n', ";")
:gsub('{%s*', "{")
:gsub(';}', "}")
:gsub('}%s*', "}")
:gsub('}\r\n', "}")
:gsub('}\n', "}")
:gsub('\r\n}', "}")
:gsub(',%s*', ",")
:gsub(':%s', ":")
:gsub('%s>%s', ">")
:gsub('#([0-f])%1([0-f])%2([0-f])%3([\\s;\\}])', "#%1%2%3%4")
return out
end
-------- delete cache file -----------
function delFile(posix, path)
for name in posix.files(path) do
if name ~= "." and name ~= ".." then
posix.unlink(path..name)
end
end
end
-------- log execute_time ---------
function logTime()
local exec_time = string .format("%.3f", ngx.now() - ngx.req.start_time())
ngx.log(ngx.CRIT, exec_time);
end
-------- log -----------
function log(data, status)
status = status or 400
ngx.log(ngx.CRIT, data)
if status ~= 0 then
ngx.exit(status)
end
end
-------- main --------
local file_lists = ngx.var.arg_f
if file_lists == nil then
log("param f is empty")
end
local types = {["js"]="application/x-javascript", ["css"]="text/css"}
local posix = require 'posix'
local root = ngx.var.document_root..'/'
local file_dir = root
local req_url = ngx.var.host..ngx.var.uri..file_lists
local uris = explode(file_lists, ",")
local size = table.getn(uris)
local cache_dir = ""
local out = ""
local ext = ""
local file_out = ""
local file_info = 0
local last_modify = 0
local http_last_modify = 0
local count = 0
-- only allow js css
ext = getFileExt(uris[1])
if types[ext] == nil then
log("extension is error")
end
-- max files
local max_files = ngx.var.max_files
if not empty(max_files) and size > tonumber(max_files) then
log("exceed max files")
end
-- set file directory
if ext == "css" then
if ngx.var.css_dir ~= nil then
file_dir = root..ngx.var.css_dir
end
elseif ngx.var.js_dir ~= nil then
file_dir = root..ngx.var.js_dir
end
if ngx.var.cache_dir ~= nil then
cache_dir = ngx.var.cache_dir.."/"
else
log("cache_dir is empty")
end
-- delete cache file
if ngx.var.arg_c ~= nil and ngx.var.admin_ip ~= nil then
local admin_ip = explode(ngx.var.admin_ip, ",");
local ip = getClientIp()
if (tableFind(admin_ip, ip)) ~= nil then
delFile(posix, cache_dir)
ngx.header.content_type = "text/html"
ngx.say('success')
ngx.exit(200)
end
end
-- get files last modify time
for i = 1, size, 1 do
file_info = posix.stat(file_dir..uris[i])
if file_info == nil then
log(uris[i].." file not found")
end
if file_info["type"] ~= "regular" or getFileExt(uris[i]) ~= ext then
log(uris[i].." extension is error")
end
if file_info["mtime"] > last_modify then
last_modify = file_info["mtime"]
end
end
ngx.header.content_type = types[ext]
http_last_modify = ngx.http_time(last_modify)
local md5_req_url = ngx.md5(req_url)
local combo_file = cache_dir..md5_req_url..".combo"
local icombo_sub = '/icombo_sub/'..md5_req_url..".combo"
local combo_modify = posix.stat(combo_file, "mtime")
-- cache nil
if nil == combo_modify then
combo_modify = 0
end
local up_cache = last_modify > combo_modify
-- cache valid
if not up_cache then
return ngx.exec(icombo_sub)
end
-- cache expire or nil
local bom = string.char(0xEF, 0xBB, 0xBF)
local dir_name = ""
local css_path_auto = ngx.var.css_path_auto
local t_out = {}
for i = 1, size, 1 do
file_out = fileRead(file_dir..uris[i])
if file_out == nil then
log(uris[i].." read fail")
elseif file_out == "" then
-- no cache file
if 0 == combo_modify then
log(uris[i].." content is empty")
else
log(uris[i].." read old cache file", 0)
return ngx.exec(icombo_sub)
end
end
if file_out:sub(1,3) == bom then
file_out = file_out:sub(4)
end
-- css_path_auto
dir_name = posix.dirname(uris[i])
if not empty(css_path_auto) and dir_name ~= nil then
-- handle ../
file_out, count = ngx.re.gsub(file_out, '../'..css_path_auto, './'..css_path_auto, 'i');
-- handle ./
file_out, count = ngx.re.gsub(file_out, '(?<!/)'..css_path_auto, dir_name..'/'..css_path_auto, 'i');
end
t_out[i] = file_out
end
-- large string use table is fast
out = table.concat(t_out, "\r\n")
t_out = nil
if ext == "css" then
-- remove css comment
local css_trim = ngx.var.css_trim
if css_trim ~= nil and css_trim:lower() == "on" then
out = removeCssComents(out)
end
-- replace images path
local css_replace = ngx.var.css_replace
if not empty(css_replace) then
local replaces = explode(css_replace, "|")
size = table.getn(replaces)
for i = 1, size, 1 do
local single = explode(replaces[i], ",")
out, count = ngx.re.gsub(out, single[1], single[2], 'i')
end
end
end
-- minify js file 修改地方开始
if ext == "js" then
-- 将js合并的字符串写入一个文件格式中
local res = fileWrite(combo_file, out)
if res == nil then
log('cache write fail, please check cache_dir permitted access')
else
-- 运行命令行程序 将文件以流的形式传入
local minifycmd = '/usr/bin/jsmin < '..combo_file
-- 返回一个文件类型
local t = io.popen(minifycmd)
-- 读取文件的所有内容 至out变量中
out = t:read("*all")
end
end
-- 修改地方结束
local res = fileWrite(combo_file, out)
if res == nil then
log('cache write fail, please check cache_dir permitted access')
end
posix.utime(combo_file, last_modify)
return ngx.exec(icombo_sub)
基本上是添加了一个判断是否传入的是js脚本,然后通过命令行的方式将js文件进行压缩。
四:添加lua的posix模块
这里介绍posix模块,它主要是可以让lua支持文件的读与写操作,这里刚好我们需要进行这类操作,因此该模块必不可少.
1.安装lua_posix模块
# wget http://git.alpinelinux.org/cgit/luaposix/snapshot/luaposix-5.1.8.tar.bz2
# tar jxvf luaposix-5.1.8.tar.bz2
# cd luaposix-5.1.8
# vi Makefile -- 打开 Makefile 文件 修改LUAINC
LUAINC= /usr/local/include/
# make CC=gcc
# make install CC=gcc
- 创建一个软连接
ln -s /usr/local/lib/lua/5.1/posix /usr/lib/lua/5.1
注意:坐着我尝试下一个最新的posix版本的模块,遗憾的是icombo.lua脚本并不支持我这么做。于是参照的是icombo作者提供的posix版本。
五:测试压缩是否有效
1. 开启nginx服务器
servicce nginx start
2.输入测试地址
http://你的ip地址/icombo/?f=css/screen.css
http://你的ip地址/icombo/?f=js/backbone.js,js/underscore.js
这样我们添加的服务就完成了。来看看效果图吧
终于这块内容结束了,于是作者简单的修改了下人家的东西,做了一个满足自己的功能。当然有些地方需要进行优化,如缓存文件的放置等等。经过作者的测试,发现使用minify的处理方式更加消耗cpu性能,但是它的压缩方式比较彻底,压缩比要好于使用nginx的压缩。这原因就在于我们没有优化吧。