📖Blog:《NodeJS模塊研究 - zlib》html
🐱Github:https://github.com/dongyuanxin/blog前端
nodejs 的 zlib 模塊提供了資源壓縮功能。例如在 http 傳輸過程當中經常使用的 gzip,能大幅度減小網絡傳輸流量,提升速度。本文將從下面幾個方面介紹 zlib 模塊和相關知識點:node
以 gzip 壓縮爲例,壓縮代碼以下:git
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。程序員
解壓剛纔壓縮後的文件,代碼以下:github
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 消息頭來告訴瀏覽器(客戶端)實際用於編碼的算法。web
服務器代碼示例以下:算法
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 字段,並進行解壓:json
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:api
zlib.createInflate()
和
zlib.createDeflate()
zlib.createGunzip()
和
zlib.createGzip()
zlib.createBrotliDecompress()
和
zlib.createBrotliCompress()
RLE 全稱是 Run Length Encoding, 行程長度編碼,也稱爲遊程編碼。它的原理是:記錄連續重複數據的出現次數。它的公式是:字符 * 出現次數
。
例如原數據是 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 文件),均可以採用哈夫曼樹進行壓縮。
👇掃碼關注「心譚博客」,查看「前端圖譜」&「算法題解」,堅持分享,共同成長👇