【js&css文件壓縮】jsMin文件壓縮及服務器配置 -3

上一次我們已經配好了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

這裏需要強調幾點知識。

  1. 管道流 < 表示input流
  2. 管道流 > 表示output流
  3. 兩個文件名不能一直,否則會導致最後文件爲空字節

我們查看一下壓縮後文件的大小。

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
  1. 創建一個軟連接
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的壓縮。這原因就在於我們沒有優化吧。

發佈了31 篇原創文章 · 獲贊 6 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章