📖Blog:《NodeJS模塊研究 - zlib》🐱Github:https://github.com/dongyuanxin/blogjavascript
nodejs 的 zlib 模塊提供了資源壓縮功能。例如在 http 傳輸過程當中經常使用的 gzip,能大幅度減小網絡傳輸流量,提升速度。本文將從下面幾個方面介紹 zlib 模塊和相關知識點:html
以 gzip 壓縮爲例,壓縮代碼以下:前端
const zlib = require("zlib"); const fs = require("fs"); const gzip = zlib.createGzip(); const rs = fs.createReadStream("./db.json"); const ws = fs.createWriteStream("./db.json.gz"); rs.pipe(gzip).pipe(ws);
以下圖所示,4.7Mb 大小的文件被壓縮到了 575Kb。java
解壓剛纔壓縮後的文件,代碼以下:node
const zlib = require("zlib"); const fs = require("fs"); const gunzip = zlib.createGunzip(); const rs = fs.createReadStream("./db.json.gz"); const ws = fs.createWriteStream("./db.json"); rs.pipe(gunzip).pipe(ws);
在服務器中和客戶端的傳輸過程當中,瀏覽器(客戶端)經過 Accept-Encoding 消息頭來告訴服務端接受的壓縮編碼,服務器經過 Content-Encoding 消息頭來告訴瀏覽器(客戶端)實際用於編碼的算法。git
服務器代碼示例以下:程序員
const zlib = require("zlib"); const fs = require("fs"); const http = require("http"); const server = http.createServer((req, res) => { const rs = fs.createReadStream("./index.html"); // 防止緩存錯亂 res.setHeader("Vary", "Accept-Encoding"); // 獲取客戶端支持的編碼 let acceptEncoding = req.headers["accept-encoding"]; if (!acceptEncoding) { acceptEncoding = ""; } // 匹配支持的壓縮格式 if (/\bdeflate\b/.test(acceptEncoding)) { res.writeHead(200, { "Content-Encoding": "deflate" }); rs.pipe(zlib.createDeflate()).pipe(res); } else if (/\bgzip\b/.test(acceptEncoding)) { res.writeHead(200, { "Content-Encoding": "gzip" }); rs.pipe(zlib.createGzip()).pipe(res); } else if (/\bbr\b/.test(acceptEncoding)) { res.writeHead(200, { "Content-Encoding": "br" }); rs.pipe(zlib.createBrotliCompress()).pipe(res); } else { res.writeHead(200, {}); rs.pipe(res); } }); server.listen(4000);
客戶端代碼就很簡單了,識別 Accept-Encoding 字段,並進行解壓:github
const zlib = require("zlib"); const http = require("http"); const fs = require("fs"); const request = http.get({ host: "localhost", path: "/index.html", port: 4000, headers: { "Accept-Encoding": "br,gzip,deflate" } }); request.on("response", response => { const output = fs.createWriteStream("example.com_index.html"); switch (response.headers["content-encoding"]) { case "br": response.pipe(zlib.createBrotliDecompress()).pipe(output); break; // 或者, 只是使用 zlib.createUnzip() 方法去處理這兩種狀況: case "gzip": response.pipe(zlib.createGunzip()).pipe(output); break; case "deflate": response.pipe(zlib.createInflate()).pipe(output); break; default: response.pipe(output); break; } });
從上面的例子能夠看出來,3 種對應的解壓/壓縮 API:算法
zlib.createInflate()
和 zlib.createDeflate()
zlib.createGunzip()
和 zlib.createGzip()
zlib.createBrotliDecompress()
和 zlib.createBrotliCompress()
RLE 全稱是 Run Length Encoding, 行程長度編碼,也稱爲遊程編碼。它的原理是:記錄連續重複數據的出現次數。它的公式是:字符 * 出現次數
。json
例如原數據是 AAAAACCPPPPPPPPERRPPP
,一共 18 個字節。按照 RLE 的規則,壓縮後的結果是:A5C2P8E1R2P3
,一共 12 個字節。壓縮比例是:12 / 17 = 70.6%
RLE 的優勢是壓縮和解壓很是快,針對連續出現的多個字符的數據壓縮率更高。但對於ABCDE
相似的數據,壓縮後數據會更大。
哈夫曼樹的原理是:出現頻率越高的字符,用盡可能更少的編碼來表示。按照這個原理,以數據ABBCCCDDDD
爲例:
字符 | 編碼(二進制) |
---|---|
D | 0 |
C | 1 |
B | 10 |
A | 11 |
原來的數據是 10 個字節。那麼編碼後的數據是:1110101110000
,一共 13bit,在計算機中須要 2 個字節來存儲。這樣的壓縮率是:2 / 10 = 20%。
可是僅僅按照這個原理編碼後的數據,沒法正確還原。之前 4bit 爲例,1110
能夠理解成:
而哈夫曼樹的設計就很巧妙,能正確還原。哈夫曼樹的構造過程以下:
不管哪一種數據類型(文本文件、圖像文件、EXE 文件),均可以採用哈夫曼樹進行壓縮。
<img src="https://tva1.sinaimg.cn/large/006tNbRwly1gbjcfr3xb5j30cw00tjrd.jpg" style="width: 100% !important;"/>
👇掃碼關注「心譚博客」,查看「前端圖譜」&「算法題解」,堅持分享,共同成長👇