Zlib#

安定性: 2 - 安定

ソースコード: lib/zlib.js

node:zlib モジュールは、Gzip、Deflate/Inflate、Brotli を使用して実装された圧縮機能を提供します。

アクセスするには

const zlib = require('node:zlib'); 

圧縮と解凍は、Node.js の Streams API をベースに構築されています。

ストリーム(ファイルなど)の圧縮または解凍は、ソースストリームをzlibTransformストリームを介して宛先ストリームにパイプすることで実行できます。

const { createGzip } = require('node:zlib');
const { pipeline } = require('node:stream');
const {
  createReadStream,
  createWriteStream,
} = require('node:fs');

const gzip = createGzip();
const source = createReadStream('input.txt');
const destination = createWriteStream('input.txt.gz');

pipeline(source, gzip, destination, (err) => {
  if (err) {
    console.error('An error occurred:', err);
    process.exitCode = 1;
  }
});

// Or, Promisified

const { promisify } = require('node:util');
const pipe = promisify(pipeline);

async function do_gzip(input, output) {
  const gzip = createGzip();
  const source = createReadStream(input);
  const destination = createWriteStream(output);
  await pipe(source, gzip, destination);
}

do_gzip('input.txt', 'input.txt.gz')
  .catch((err) => {
    console.error('An error occurred:', err);
    process.exitCode = 1;
  }); 

単一ステップでデータの圧縮または解凍を行うことも可能です。

const { deflate, unzip } = require('node:zlib');

const input = '.................................';
deflate(input, (err, buffer) => {
  if (err) {
    console.error('An error occurred:', err);
    process.exitCode = 1;
  }
  console.log(buffer.toString('base64'));
});

const buffer = Buffer.from('eJzT0yMAAGTvBe8=', 'base64');
unzip(buffer, (err, buffer) => {
  if (err) {
    console.error('An error occurred:', err);
    process.exitCode = 1;
  }
  console.log(buffer.toString());
});

// Or, Promisified

const { promisify } = require('node:util');
const do_unzip = promisify(unzip);

do_unzip(buffer)
  .then((buf) => console.log(buf.toString()))
  .catch((err) => {
    console.error('An error occurred:', err);
    process.exitCode = 1;
  }); 

スレッドプールの使用とパフォーマンスに関する考慮事項#

明示的に同期的なもの以外は、すべてのzlib APIはNode.jsの内部スレッドプールを使用します。これは、一部のアプリケーションでは予期しない効果やパフォーマンスの制限につながる可能性があります。

多数のzlibオブジェクトを同時に作成して使用すると、深刻なメモリ断片化が発生する可能性があります。

const zlib = require('node:zlib');

const payload = Buffer.from('This is some data');

// WARNING: DO NOT DO THIS!
for (let i = 0; i < 30000; ++i) {
  zlib.deflate(payload, (err, buffer) => {});
} 

上記の例では、30,000個のdeflateインスタンスが同時に作成されています。一部のオペレーティングシステムのメモリ割り当てと解放の処理方法により、これにより深刻なメモリ断片化が発生する可能性があります。

労力の重複を避けるために、圧縮操作の結果をキャッシュすることを強くお勧めします。

HTTPリクエストとレスポンスの圧縮#

node:zlibモジュールを使用して、HTTPで定義されているgzipdeflatebrコンテンツエンコーディングメカニズムのサポートを実装できます。

HTTPのAccept-Encodingヘッダーは、HTTPリクエスト内でクライアントが受け入れる圧縮エンコーディングを識別するために使用されます。Content-Encodingヘッダーは、メッセージに実際に適用された圧縮エンコーディングを識別するために使用されます。

以下に示す例は、基本的な概念を示すために大幅に簡略化されています。zlibエンコーディングはコストがかかる可能性があり、結果はキャッシュする必要があります。zlibの使用に関わる速度/メモリ/圧縮のトレードオフの詳細については、メモリ使用量の調整を参照してください。

// Client request example
const zlib = require('node:zlib');
const http = require('node:http');
const fs = require('node:fs');
const { pipeline } = require('node:stream');

const request = http.get({ host: 'example.com',
                           path: '/',
                           port: 80,
                           headers: { 'Accept-Encoding': 'br,gzip,deflate' } });
request.on('response', (response) => {
  const output = fs.createWriteStream('example.com_index.html');

  const onError = (err) => {
    if (err) {
      console.error('An error occurred:', err);
      process.exitCode = 1;
    }
  };

  switch (response.headers['content-encoding']) {
    case 'br':
      pipeline(response, zlib.createBrotliDecompress(), output, onError);
      break;
    // Or, just use zlib.createUnzip() to handle both of the following cases:
    case 'gzip':
      pipeline(response, zlib.createGunzip(), output, onError);
      break;
    case 'deflate':
      pipeline(response, zlib.createInflate(), output, onError);
      break;
    default:
      pipeline(response, output, onError);
      break;
  }
}); 
// server example
// Running a gzip operation on every request is quite expensive.
// It would be much more efficient to cache the compressed buffer.
const zlib = require('node:zlib');
const http = require('node:http');
const fs = require('node:fs');
const { pipeline } = require('node:stream');

http.createServer((request, response) => {
  const raw = fs.createReadStream('index.html');
  // Store both a compressed and an uncompressed version of the resource.
  response.setHeader('Vary', 'Accept-Encoding');
  let acceptEncoding = request.headers['accept-encoding'];
  if (!acceptEncoding) {
    acceptEncoding = '';
  }

  const onError = (err) => {
    if (err) {
      // If an error occurs, there's not much we can do because
      // the server has already sent the 200 response code and
      // some amount of data has already been sent to the client.
      // The best we can do is terminate the response immediately
      // and log the error.
      response.end();
      console.error('An error occurred:', err);
    }
  };

  // Note: This is not a conformant accept-encoding parser.
  // See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3
  if (/\bdeflate\b/.test(acceptEncoding)) {
    response.writeHead(200, { 'Content-Encoding': 'deflate' });
    pipeline(raw, zlib.createDeflate(), response, onError);
  } else if (/\bgzip\b/.test(acceptEncoding)) {
    response.writeHead(200, { 'Content-Encoding': 'gzip' });
    pipeline(raw, zlib.createGzip(), response, onError);
  } else if (/\bbr\b/.test(acceptEncoding)) {
    response.writeHead(200, { 'Content-Encoding': 'br' });
    pipeline(raw, zlib.createBrotliCompress(), response, onError);
  } else {
    response.writeHead(200, {});
    pipeline(raw, response, onError);
  }
}).listen(1337); 

デフォルトでは、zlibメソッドは切り詰められたデータを解凍するときにエラーをスローします。ただし、データが不完全であることがわかっている場合、または圧縮ファイルの先頭部分のみを検査したい場合は、使用されるフラッシュメソッドを変更することで、デフォルトのエラー処理を抑制できます。

// This is a truncated version of the buffer from the above examples
const buffer = Buffer.from('eJzT0yMA', 'base64');

zlib.unzip(
  buffer,
  // For Brotli, the equivalent is zlib.constants.BROTLI_OPERATION_FLUSH.
  { finishFlush: zlib.constants.Z_SYNC_FLUSH },
  (err, buffer) => {
    if (err) {
      console.error('An error occurred:', err);
      process.exitCode = 1;
    }
    console.log(buffer.toString());
  }); 

これは、入力データの形式が無効な場合など、その他のエラーをスローする状況での動作は変更しません。このメソッドを使用すると、入力データが途中で終了したのか、整合性チェックが不足しているのかを判断できなくなるため、解凍された結果が有効であることを手動で確認する必要があります。

メモリ使用量の調整#

zlibベースのストリームの場合#

zlib/zconf.hから、Node.jsの使用に合わせて修正

deflateのメモリ要件(バイト単位)は次のとおりです。

(1 << (windowBits + 2)) + (1 << (memLevel + 9)) 

つまり:windowBits = 15の場合128KB + memLevel = 8の場合128KB(デフォルト値)+ 小さなオブジェクト用の数キロバイトです。

たとえば、デフォルトのメモリ要件を256KBから128KBに削減するには、オプションを次のように設定する必要があります。

const options = { windowBits: 14, memLevel: 7 }; 

ただし、これにより一般的に圧縮率が低下します。

inflateのメモリ要件(バイト単位)は1 << windowBitsです。つまり、windowBits = 15(デフォルト値)の場合32KB + 小さなオブジェクト用の数キロバイトです。

これは、デフォルトで16KBのサイズの内部出力スラブバッファ1つに追加されます。

zlib圧縮の速度は、level設定によって最も劇的に影響を受けます。レベルが高いほど圧縮率は向上しますが、完了するまで時間がかかります。レベルが低いほど圧縮率は低下しますが、はるかに高速になります。

一般的に、メモリ使用量が多いオプションは、各write操作でより多くのデータ処理が可能になるため、Node.jsがzlibを呼び出す回数を少なくなることを意味します。そのため、これはメモリ使用量を犠牲にして速度に影響を与えるもう1つの要因です。

Brotliベースのストリームの場合#

Brotliベースのストリームにはzlibオプションに相当するものがありますが、これらのオプションの範囲はzlibのものとは異なります。

  • zlibのlevelオプションは、BrotliのBROTLI_PARAM_QUALITYオプションに対応します。
  • zlibのwindowBitsオプションは、BrotliのBROTLI_PARAM_LGWINオプションに対応します。

Brotli固有のオプションの詳細については、下記を参照してください。

フラッシュ#

.flush()を圧縮ストリームで呼び出すと、zlibは現在可能な限り多くの出力を返します。これは圧縮品質の低下を犠牲にする可能性がありますが、データをできるだけ早く利用する必要がある場合に役立ちます。

次の例では、flush()を使用して圧縮された部分的なHTTPレスポンスをクライアントに書き込んでいます。

const zlib = require('node:zlib');
const http = require('node:http');
const { pipeline } = require('node:stream');

http.createServer((request, response) => {
  // For the sake of simplicity, the Accept-Encoding checks are omitted.
  response.writeHead(200, { 'content-encoding': 'gzip' });
  const output = zlib.createGzip();
  let i;

  pipeline(output, response, (err) => {
    if (err) {
      // If an error occurs, there's not much we can do because
      // the server has already sent the 200 response code and
      // some amount of data has already been sent to the client.
      // The best we can do is terminate the response immediately
      // and log the error.
      clearInterval(i);
      response.end();
      console.error('An error occurred:', err);
    }
  });

  i = setInterval(() => {
    output.write(`The current time is ${Date()}\n`, () => {
      // The data has been passed to zlib, but the compression algorithm may
      // have decided to buffer the data for more efficient compression.
      // Calling .flush() will make the data available as soon as the client
      // is ready to receive it.
      output.flush();
    });
  }, 1000);
}).listen(1337); 

定数#

zlib定数#

zlib.hで定義されているすべての定数は、require('node:zlib').constantsにも定義されています。通常の操作では、これらの定数を使用する必要はありません。存在が驚くべきものでないように、ドキュメント化されています。このセクションは、zlibのドキュメントからほぼそのまま引用されています。

以前は、定数はrequire('node:zlib')から直接アクセスできました(例:zlib.Z_NO_FLUSH)。モジュールから定数に直接アクセスすることは現在も可能ですが、非推奨です。

許可されたフラッシュ値。

  • zlib.constants.Z_NO_FLUSH
  • zlib.constants.Z_PARTIAL_FLUSH
  • zlib.constants.Z_SYNC_FLUSH
  • zlib.constants.Z_FULL_FLUSH
  • zlib.constants.Z_FINISH
  • zlib.constants.Z_BLOCK
  • zlib.constants.Z_TREES

圧縮/解凍関数の戻り値コード。負の値はエラーを示し、正の値は特別な正常イベントに使用されます。

  • zlib.constants.Z_OK
  • zlib.constants.Z_STREAM_END
  • zlib.constants.Z_NEED_DICT
  • zlib.constants.Z_ERRNO
  • zlib.constants.Z_STREAM_ERROR
  • zlib.constants.Z_DATA_ERROR
  • zlib.constants.Z_MEM_ERROR
  • zlib.constants.Z_BUF_ERROR
  • zlib.constants.Z_VERSION_ERROR

圧縮レベル。

  • zlib.constants.Z_NO_COMPRESSION
  • zlib.constants.Z_BEST_SPEED
  • zlib.constants.Z_BEST_COMPRESSION
  • zlib.constants.Z_DEFAULT_COMPRESSION

圧縮戦略。

  • zlib.constants.Z_FILTERED
  • zlib.constants.Z_HUFFMAN_ONLY
  • zlib.constants.Z_RLE
  • zlib.constants.Z_FIXED
  • zlib.constants.Z_DEFAULT_STRATEGY

Brotli定数#

Brotliベースのストリームには、いくつかのオプションとその他の定数が用意されています。

フラッシュ操作#

Brotliベースのストリームに対して有効なフラッシュ操作の値を以下に示します。

  • zlib.constants.BROTLI_OPERATION_PROCESS (すべての操作のデフォルト)
  • zlib.constants.BROTLI_OPERATION_FLUSH.flush() を呼び出す際のデフォルト)
  • zlib.constants.BROTLI_OPERATION_FINISH (最後のチャンクのデフォルト)
  • zlib.constants.BROTLI_OPERATION_EMIT_METADATA
    • この特定の操作は、Node.jsのコンテキストでは使いにくい場合があります。ストリーミングレイヤーにより、どのデータがこのフレームに最終的に含まれるかが分かりにくいからです。また、現在、Node.js APIを通じてこのデータを使用する方法はありません。
圧縮器オプション#

Brotliエンコーダーに設定できるいくつかのオプションがあり、圧縮効率と速度に影響します。キーと値の両方に、zlib.constantsオブジェクトのプロパティとしてアクセスできます。

最も重要なオプションは次のとおりです。

  • BROTLI_PARAM_MODE
    • BROTLI_MODE_GENERIC (デフォルト)
    • BROTLI_MODE_TEXT 、UTF-8テキストに合わせて調整済み
    • BROTLI_MODE_FONT 、WOFF 2.0フォントに合わせて調整済み
  • BROTLI_PARAM_QUALITY
    • BROTLI_MIN_QUALITY から BROTLI_MAX_QUALITY の範囲で、デフォルトは BROTLI_DEFAULT_QUALITY です。
  • BROTLI_PARAM_SIZE_HINT
    • 予想される入力サイズを表す整数値。入力サイズが不明な場合はデフォルトで0

圧縮アルゴリズムとメモリ使用量の調整に関する高度な制御のために、次のフラグを設定できます。

  • BROTLI_PARAM_LGWIN
    • BROTLI_MIN_WINDOW_BITS から BROTLI_MAX_WINDOW_BITS の範囲で、デフォルトは BROTLI_DEFAULT_WINDOW です。BROTLI_PARAM_LARGE_WINDOW フラグが設定されている場合は、BROTLI_LARGE_MAX_WINDOW_BITS まで使用できます。
  • BROTLI_PARAM_LGBLOCK
    • BROTLI_MIN_INPUT_BLOCK_BITS から BROTLI_MAX_INPUT_BLOCK_BITS の範囲です。
  • BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING
    • 圧縮率を低下させて解凍速度を向上させるブール型フラグ。
  • BROTLI_PARAM_LARGE_WINDOW
    • 「Large Window Brotli」モードを有効にするブール型フラグ(RFC 7932で標準化されたBrotli形式とは互換性がありません)。
  • BROTLI_PARAM_NPOSTFIX
    • 0 から BROTLI_MAX_NPOSTFIX の範囲です。
  • BROTLI_PARAM_NDIRECT
    • 0 から 15 << NPOSTFIX の範囲で、1 << NPOSTFIX のステップです。
解凍器オプション#

解凍を制御するために、次の高度なオプションを使用できます。

  • BROTLI_DECODER_PARAM_DISABLE_RING_BUFFER_REALLOCATION
    • 内部メモリ割り当てパターンに影響を与えるブール型フラグ。
  • BROTLI_DECODER_PARAM_LARGE_WINDOW
    • 「Large Window Brotli」モードを有効にするブール型フラグ(RFC 7932で標準化されたBrotli形式とは互換性がありません)。

クラス: Options#

各zlibベースのクラスは、optionsオブジェクトを受け取ります。オプションは必須ではありません。

一部のオプションは圧縮時のみ関連し、解凍クラスでは無視されます。

詳細については、deflateInit2inflateInit2のドキュメントを参照してください。

クラス: BrotliOptions#

各Brotliベースのクラスは、optionsオブジェクトを受け取ります。すべてのオプションはオプションです。

例:

const stream = zlib.createBrotliCompress({
  chunkSize: 32 * 1024,
  params: {
    [zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_TEXT,
    [zlib.constants.BROTLI_PARAM_QUALITY]: 4,
    [zlib.constants.BROTLI_PARAM_SIZE_HINT]: fs.statSync(inputFile).size,
  },
}); 

クラス: zlib.BrotliCompress#

Brotliアルゴリズムを使用してデータを圧縮します。

クラス: zlib.BrotliDecompress#

Brotliアルゴリズムを使用してデータを解凍します。

クラス: zlib.Deflate#

deflateを使用してデータを圧縮します。

クラス: zlib.DeflateRaw#

deflateを使用してデータを圧縮し、zlibヘッダーを追加しません。

クラス: zlib.Gunzip#

gzipストリームを解凍します。

クラス: zlib.Gzip#

gzipを使用してデータを圧縮します。

クラス: zlib.Inflate#

deflateストリームを解凍します。

クラス: zlib.InflateRaw#

raw deflateストリームを解凍します。

クラス: zlib.Unzip#

ヘッダーを自動検出して、GzipまたはDeflateで圧縮されたストリームを解凍します。

クラス: zlib.ZlibBase#

node:zlibモジュールではエクスポートされません。圧縮/解凍クラスの基底クラスであるため、ここに記載されています。

このクラスはstream.Transformを継承しており、node:zlibオブジェクトをパイプや同様のストリーム操作で使用できます。

zlib.bytesRead#

安定性: 0 - 非推奨: 代わりにzlib.bytesWrittenを使用してください。

zlib.bytesWrittenの非推奨のエイリアス。この元の名前は、エンジンによって読み取られたバイト数と解釈することも意味がありましたが、これらの名前の下で値を公開するNode.jsの他のストリームとは矛盾しています。

zlib.bytesWritten#

zlib.bytesWritten プロパティは、バイトが処理される(派生クラスに適した圧縮または解凍)前に、エンジンに書き込まれたバイト数を指定します。

zlib.close([callback])#

基礎となるハンドルを閉じます。

zlib.flush([kind, ]callback)#

  • kind デフォルト: zlibベースのストリームの場合はzlib.constants.Z_FULL_FLUSH、Brotliベースのストリームの場合はzlib.constants.BROTLI_OPERATION_FLUSH
  • callback <Function>

保留中のデータをフラッシュします。安易に呼び出さないでください。早すぎるフラッシュは、圧縮アルゴリズムの効率に悪影響を及ぼします。

この関数を呼び出すと、内部zlib状態からのデータのみがフラッシュされ、ストリームレベルでのフラッシュは実行されません。むしろ、.write()への通常の呼び出しのように動作し、つまり、他の保留中の書き込みの後ろにキューに入れられ、ストリームからデータが読み取られるときにのみ出力が生成されます。

zlib.params(level, strategy, callback)#

この関数は、zlibベースのストリーム(Brotliではない)でのみ使用可能です。

圧縮レベルと圧縮戦略を動的に更新します。deflateアルゴリズムにのみ適用可能です。

zlib.reset()#

コンプレッサー/デコンプレッサーを工場出荷時のデフォルトにリセットします。inflateおよびdeflateアルゴリズムにのみ適用可能です。

zlib.constants#

Zlib関連の定数を列挙するオブジェクトを提供します。

zlib.createBrotliCompress([options])#

新しいBrotliCompressオブジェクトを作成して返します。

zlib.createBrotliDecompress([options])#

新しいBrotliDecompressオブジェクトを作成して返します。

zlib.createDeflate([options])#

新しいDeflateオブジェクトを作成して返します。

zlib.createDeflateRaw([options])#

新しいDeflateRawオブジェクトを作成して返します。

zlibを1.2.8から1.2.11にアップグレードすると、windowBitsがraw deflateストリームに対して8に設定されている場合の動作が変更されました。zlibは、当初8に設定されていた場合、windowBitsを自動的に9に設定していました。新しいバージョンのzlibは例外をスローするため、Node.jsは8の値を9にアップグレードする元の動作を復元しました。これは、zlibにwindowBits = 9を渡すと、実際には8ビットウィンドウのみを効果的に使用する圧縮ストリームが生成されるためです。

zlib.createGunzip([options])#

新しいGunzipオブジェクトを作成して返します。

zlib.createGzip([options])#

新しいGzipオブジェクトを作成して返します。を参照してください。

zlib.createInflate([options])#

新しいInflateオブジェクトを作成して返します。

zlib.createInflateRaw([options])#

新しいInflateRawオブジェクトを作成して返します。

zlib.createUnzip([options])#

新しいUnzipオブジェクトを作成して返します。

便利なメソッド#

これらはすべて、最初の引数としてBufferTypedArrayDataViewArrayBuffer、または文字列を、2番目の引数としてオプションをzlibクラスに提供し、callback(error, result)で指定されたコールバックを呼び出します。

すべてのメソッドには、同じ引数を受け取るがコールバックを持たない*Sync対応物があります。

zlib.brotliCompress(buffer[, options], callback)#

zlib.brotliCompressSync(buffer[, options])#

BrotliCompressを使用してデータチャンクを圧縮します。

zlib.brotliDecompress(buffer[, options], callback)#

zlib.brotliDecompressSync(buffer[, options])#

BrotliDecompressを使用してデータチャンクを解凍します。

zlib.deflate(buffer[, options], callback)#

zlib.deflateSync(buffer[, options])#

Deflateを使用してデータチャンクを圧縮します。

zlib.deflateRaw(buffer[, options], callback)#

zlib.deflateRawSync(buffer[, options])#

DeflateRawを使用してデータチャンクを圧縮します。

zlib.gunzip(buffer[, options], callback)#

zlib.gunzipSync(buffer[, options])#

Gunzipを使用してデータチャンクを解凍します。

zlib.gzip(buffer[, options], callback)#

zlib.gzipSync(buffer[, options])#

Gzipを使用してデータチャンクを圧縮します。

zlib.inflate(buffer[, options], callback)#

zlib.inflateSync(buffer[, options])#

Inflateを使用してデータチャンクを解凍します。

zlib.inflateRaw(buffer[, options], callback)#

zlib.inflateRawSync(buffer[, options])#

InflateRawを使用してデータチャンクを解凍します。

zlib.unzip(buffer[, options], callback)#

zlib.unzipSync(buffer[, options])#

Unzipを使用してデータチャンクを解凍します。