1. 程式人生 > >在Nodejs或者express的response中設定 'content-encoding': 'gzip'

在Nodejs或者express的response中設定 'content-encoding': 'gzip'

環境: Nodejs/express + Apache

如題,如果在後端的express server中,需要讀取server上的大檔案或者需要返回的資料為比較大的物件時(比如幾十M的資料時),考慮用stream/pipe的方式處理data,流方式提供一些實用的高效的工具來處理比較大的data的處理,之後可以直接輸出給response; 但是需要了解的是,最好是以壓縮比如gzip或者deflate的方式返回給瀏覽器

或者請求的所有資料統統以壓縮的形式返回給瀏覽器,所以最主要的前提是,根據使用者的瀏覽器判斷,當前這個client端的browser的請求是支援哪種型別的解壓縮,比如gzip或者是deflate等,這裡壓縮的倍率大概為,壓縮前: 50M左右, 壓縮後3M左右,基本會有10倍多的壓縮率,所以在網路間傳輸時效能會有很大提升

.

這裡的例子是用nodejs的,我理解用其他的語言比如java,道理也是一樣的,語言只是封裝和實現了tcp,http協議,優化的最後都是要了解到這些。

>>為什麼要壓縮,這裡講的很清楚 --> https://blog.csdn.net/liangxw1/article/details/84835199

// 用stream/pipe的方式返回data在response裡,需要在http的response header中的 Accept-Encoding: gzip或者deflate,可以做到在server端壓縮,在browser client端解壓縮data並顯示. 程式碼如下

module.exports.getTest = function (req, res) {
    // A
    // filepath, 這裡filepath路徑對應的檔案為 *.json.gz的型別
    // 注,這裡因為已經把json資料壓縮成*.json.gz了,所以直接用stream/pipe讀到response返回即可,
    // 因為瀏覽器自己可以解析(解壓縮)*.json.gz檔案為json資料
    // 
    // 這裡我認為是這樣的: 當需要處理讀取大檔案或者比較大的資料時,用正常的res.json(data)返回資料,這可能會有效能問題,
    //   但是用stream/pipe這種流的方式處理大檔案或者資料時,是有優勢的,可以提高處理效能,
    //   但是,這種流的方式放到response後,只用如下 Apache的配置並沒有起到gzip或者deflate的壓縮效果,也許需要如下設定??還不確定,***有時間在本機測試一下***
    //    AddOutputFilterByType DEFLATE application/octet-stream??
    //    所以這裡就 顯示的 用如下的res.writeHead的方式手動設定content-encoding,即可讓這裡壓縮的*.json.gz傳輸給瀏覽器並解壓縮,的節省頻寬,提高效能的效果
    fs.exists(filepath, function (exists) {
        if (exists === true) {

            const srcReadStream = fs.createReadStream(filepath);

            // Success with response body
            // res.status(200);
            // 注,這裡只是例子程式碼,這裡應該根據使用者的瀏覽器所支援的解壓縮型別是gzip還是deflate來設定content-encoding
            res.writeHead(200, { 'content-encoding': 'gzip' });
            srcReadStream.pipe(res);
            
            srcReadStream.on('close', function () {
                // It can go here after close event
                srcReadStream.destroy();
            });
            
            srcReadStream.on('error', function (err) {
                srcReadStream.destroy();
            });
        } else {

            ...
        }
    });
}

 

檢視相關資料,讓資料在返回的response中以壓縮的形式返回,至少可以在如下兩個地方做:

  1. 在應用程式的程式碼中,判斷請求的http header中的 Accept-Encoding: gzip, deflate 支援什麼型別的壓縮和解壓縮,之後在返回的response中設定Content-Encoding: gzip或者deflate等

         在nodejs/express中,compression這個外掛可以做到壓縮後在傳輸給client browser. 之後browser可以正確解壓縮並顯示

         但是這種方式,個人不是很推薦,因為對nodejs來說大量的處理壓縮的動作可能會影響效能(??),另外如果可以在Apache伺服器上做,應該會更好,首先對nodejs的壓力小,程式碼不用改,侵入性小,在Apache上配置也相對靈活

    2. 在Apache伺服器上,啟用如下配置

https://httpd.apache.org/docs/2.4/mod/mod_deflate.html

https://varvy.com/pagespeed/enable-compression.html

LoadModule deflate_module modules/mod_deflate.so

// 下面的各項需要壓縮什麼型別的資料就開啟相應的行
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/x-javascript application/javascript application/ecmascript
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/html text/plain text/xml
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml

// 下面的配置,暫時不理解,待深入調查??
DeflateFilterNote Input instream
DeflateFilterNote Output outstream
DeflateFilterNote Ratio ratio

如果不用stream/pipe的方式返回data在response,而是用expess的res.json(data), 用上面的#1,#2兩種方式都可以做到客戶端和server端壓縮資料在傳輸和解壓縮的效果.

下面這種方式的stream/pipe方式是可以的,這個類似於上面第一種方式

// 參考: https://stackoverflow.com/questions/3894794/node-js-gzip-compression

// server example
// Running a gzip operation on every request is quite expensive.
// It would be much more efficient to cache the compressed buffer.
var zlib = require('zlib');
var http = require('http');
var fs = require('fs');
http.createServer(function(request, response) {
  var raw = fs.createReadStream('sample-drag.html');
  var acceptEncoding = request.headers['accept-encoding'];
  if (!acceptEncoding) {
    acceptEncoding = '';
  }

  // Note: this is not a conformant accept-encoding parser.
  // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3
  if (acceptEncoding.match(/\bgzip\b/)) {
    response.writeHead(200, { 'content-encoding': 'gzip' });
    raw.pipe(zlib.createGzip()).pipe(response);
  } else if (acceptEncoding.match(/\bdeflate\b/)) {
    response.writeHead(200, { 'content-encoding': 'deflate' });
    raw.pipe(zlib.createDeflate()).pipe(response);
  } else {
    response.writeHead(200, {});
    raw.pipe(response);
  }
}).listen(1337);


// 另一個例子
exports.helloWorld = function helloWorld(req, res) {
  const zlib = require('zlib');

  // Obtain JSON stream from your source...

  res.status(200);

  res.set('Content-Type', 'text/plain');
  res.set('Content-Encoding', 'gzip');

  json.pipe(zlib.createGzip()).pipe(res);
};