上一次我們已經配好了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的壓縮。這原因就在於我們沒有優化吧。