Node.js zlib モジュール
zlib は Node.js に組み込まれたコアモジュールで、データ圧縮と解凍機能を提供します。広く使用されている圧縮ライブラリである zlib ライブラリに基づいており、次のような複数の圧縮形式をサポートしています:
- Gzip:最も一般的な圧縮形式
- Deflate:もう一つの人気のある圧縮アルゴリズム
- Brotli:Google が開発した新しい圧縮アルゴリズム
データ圧縮が必要な理由
ネットワーク転送やファイル保存において、データ圧縮には以下のような利点があります:
- 帯域幅の消費を削減:圧縮後のデータはサイズが小さくなり、転送が高速化します
- 保存容量を節約:圧縮ファイルはディスク容量をより少なく使用します
- パフォーマンスの向上:圧縮・解凍には CPU リソースが必要ですが、通常ネットワーク I/O の節約の方が顕著です
基本的な使用方法
データの圧縮
例
const zlib = require('zlib');
const fs = require('fs');
// 読み取りストリームを作成
const input = fs.createReadStream('input.txt');
// Gzip 圧縮ストリームを作成
const gzip = zlib.createGzip();
// 書き込みストリームを作成
const output = fs.createWriteStream('input.txt.gz');
// パイプライン接続:読み取り -> 圧縮 -> 書き込み
input.pipe(gzip).pipe(output);
const fs = require('fs');
// 読み取りストリームを作成
const input = fs.createReadStream('input.txt');
// Gzip 圧縮ストリームを作成
const gzip = zlib.createGzip();
// 書き込みストリームを作成
const output = fs.createWriteStream('input.txt.gz');
// パイプライン接続:読み取り -> 圧縮 -> 書き込み
input.pipe(gzip).pipe(output);
データの解凍
例
const zlib = require('zlib');
const fs = require('fs');
// 読み取りストリームを作成(圧縮ファイル)
const input = fs.createReadStream('input.txt.gz');
// Gunzip 解凍ストリームを作成
const gunzip = zlib.createGunzip();
// 書き込みストリームを作成
const output = fs.createWriteStream('input.txt');
// パイプライン接続:読み取り -> 解凍 -> 書き込み
input.pipe(gunzip).pipe(output);
const fs = require('fs');
// 読み取りストリームを作成(圧縮ファイル)
const input = fs.createReadStream('input.txt.gz');
// Gunzip 解凍ストリームを作成
const gunzip = zlib.createGunzip();
// 書き込みストリームを作成
const output = fs.createWriteStream('input.txt');
// パイプライン接続:読み取り -> 解凍 -> 書き込み
input.pipe(gunzip).pipe(output);
圧縮メソッドの詳細
zlib モジュールは、さまざまなシーンに適した複数の圧縮メソッドを提供しています:
同期メソッド
少量のデータの処理に適しており、操作は簡単ですがイベントループをブロックします:
例
const zlib = require('zlib');
const input = '圧縮が必要なテキストデータ';
// 同期圧縮
const compressed = zlib.deflateSync(input);
console.log('圧縮後:', compressed.toString('base64'));
// 同期解凍
const decompressed = zlib.inflateSync(compressed);
console.log('解凍後:', decompressed.toString());
const input = '圧縮が必要なテキストデータ';
// 同期圧縮
const compressed = zlib.deflateSync(input);
console.log('圧縮後:', compressed.toString('base64'));
// 同期解凍
const decompressed = zlib.inflateSync(compressed);
console.log('解凍後:', decompressed.toString());
非同期メソッド
大量のデータの処理に適しており、イベントループをブロックしません:
例
const zlib = require('zlib');
const input = '圧縮が必要なテキストデータ';
// 非同期圧縮
zlib.deflate(input, (err, compressed) => {
if (!err) {
console.log('圧縮後:', compressed.toString('base64'));
// 非同期解凍
zlib.inflate(compressed, (err, decompressed) => {
if (!err) {
console.log('解凍後:', decompressed.toString());
}
});
}
});
const input = '圧縮が必要なテキストデータ';
// 非同期圧縮
zlib.deflate(input, (err, compressed) => {
if (!err) {
console.log('圧縮後:', compressed.toString('base64'));
// 非同期解凍
zlib.inflate(compressed, (err, decompressed) => {
if (!err) {
console.log('解凍後:', decompressed.toString());
}
});
}
});
ストリーム処理
大きなファイルやネットワークデータの処理に最も適しており、メモリ効率が高いです:
例
const zlib = require('zlib');
const fs = require('fs');
// 変換ストリームを作成
const gzip = zlib.createGzip({
level: zlib.constants.Z_BEST_COMPRESSION // 最高圧縮レベル
});
fs.createReadStream('largefile.txt')
.pipe(gzip)
.pipe(fs.createWriteStream('largefile.txt.gz'))
.on('finish', () => console.log('圧縮完了'));
const fs = require('fs');
// 変換ストリームを作成
const gzip = zlib.createGzip({
level: zlib.constants.Z_BEST_COMPRESSION // 最高圧縮レベル
});
fs.createReadStream('largefile.txt')
.pipe(gzip)
.pipe(fs.createWriteStream('largefile.txt.gz'))
.on('finish', () => console.log('圧縮完了'));
高度な設定オプション
zlib の圧縮メソッドは設定オブジェクトを受け取って、圧縮動作をカスタマイズできます:
一般的な設定パラメータ
例
{
level: zlib.constants.Z_DEFAULT_COMPRESSION, // 圧縮レベル
memLevel: 8, // メモリ使用レベル
strategy: zlib.constants.Z_DEFAULT_STRATEGY, // 圧縮ストラテジー
windowBits: 15, // ウィンドウサイズ
chunkSize: 16 * 1024, // ブロックサイズ
dictionary: null // プリセット辞書
}
level: zlib.constants.Z_DEFAULT_COMPRESSION, // 圧縮レベル
memLevel: 8, // メモリ使用レベル
strategy: zlib.constants.Z_DEFAULT_STRATEGY, // 圧縮ストラテジー
windowBits: 15, // ウィンドウサイズ
chunkSize: 16 * 1024, // ブロックサイズ
dictionary: null // プリセット辞書
}
圧縮レベルの説明
| レベル定数 | 值 | 説明 |
|---|---|---|
| Z_NO_COMPRESSION | 0 | 圧縮しない |
| Z_BEST_SPEED | 1 | 最速、最低圧縮率 |
| Z_BEST_COMPRESSION | 9 | 最高圧縮率、最遅速度 |
| Z_DEFAULT_COMPRESSION | -1 | デフォルトのバランス方式 (通常=6) |
実際の適用シーン
HTTPレスポンスの圧縮
Webサーバーで HTTPレスポンスを圧縮すると、転送データ量を大幅に削減できます:
例
const http = require('http');
const zlib = require('zlib');
http.createServer((req, res) => {
const responseText = 'これは圧縮が必要なレスポンステキストです'.repeat(100);
// クライアントが gzip 圧縮を受け付けるかどうかを確認
if (req.headers['accept-encoding'] && req.headers['accept-encoding'].includes('gzip')) {
res.writeHead(200, {
'Content-Encoding': 'gzip',
'Content-Type': 'text/plain'
});
zlib.gzip(responseText, (err, compressed) => {
res.end(compressed);
});
} else {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(responseText);
}
}).listen(3000);
const zlib = require('zlib');
http.createServer((req, res) => {
const responseText = 'これは圧縮が必要なレスポンステキストです'.repeat(100);
// クライアントが gzip 圧縮を受け付けるかどうかを確認
if (req.headers['accept-encoding'] && req.headers['accept-encoding'].includes('gzip')) {
res.writeHead(200, {
'Content-Encoding': 'gzip',
'Content-Type': 'text/plain'
});
zlib.gzip(responseText, (err, compressed) => {
res.end(compressed);
});
} else {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(responseText);
}
}).listen(3000);
ファイル圧縮アーカイブ
複数のファイルを一括圧縮:
例
const zlib = require('zlib');
const fs = require('fs');
const path = require('path');
const { pipeline } = require('stream');
const files = ['file1.txt', 'file2.txt', 'file3.txt'];
files.forEach(file => {
const input = fs.createReadStream(file);
const output = fs.createWriteStream(`${file}.gz`);
pipeline(
input,
zlib.createGzip(),
output,
(err) => {
if (err) console.error(`圧縮 ${file}失敗:`, err);
else console.log(`成功圧縮 ${file}`);
}
);
});
const fs = require('fs');
const path = require('path');
const { pipeline } = require('stream');
const files = ['file1.txt', 'file2.txt', 'file3.txt'];
files.forEach(file => {
const input = fs.createReadStream(file);
const output = fs.createWriteStream(`${file}.gz`);
pipeline(
input,
zlib.createGzip(),
output,
(err) => {
if (err) console.error(`圧縮 ${file}失敗:`, err);
else console.log(`成功圧縮 ${file}`);
}
);
});
データベース保存の最適化
大型の JSON データを圧縮して保存:
例
const zlib = require('zlib');
const fs = require('fs');
const largeData = {
/* 大型 JSON オブジェクト */
};
// 圧縮して保存
zlib.deflate(JSON.stringify(largeData), (err, compressed) => {
if (!err) {
fs.writeFile('data.json.deflate', compressed, (err) => {
if (!err) console.log('データ圧縮保存成功');
});
}
});
// 読み取り時に解凍
fs.readFile('data.json.deflate', (err, data) => {
if (!err) {
zlib.inflate(data, (err, decompressed) => {
if (!err) {
const originalData = JSON.parse(decompressed.toString());
console.log('データ復元成功');
}
});
}
});
const fs = require('fs');
const largeData = {
/* 大型 JSON オブジェクト */
};
// 圧縮して保存
zlib.deflate(JSON.stringify(largeData), (err, compressed) => {
if (!err) {
fs.writeFile('data.json.deflate', compressed, (err) => {
if (!err) console.log('データ圧縮保存成功');
});
}
});
// 読み取り時に解凍
fs.readFile('data.json.deflate', (err, data) => {
if (!err) {
zlib.inflate(data, (err, decompressed) => {
if (!err) {
const originalData = JSON.parse(decompressed.toString());
console.log('データ復元成功');
}
});
}
});
パフォーマンス最適化のアドバイス
適切な圧縮レベルを選択する
- ネットワーク転送:Z_BEST_SPEED (1) から Z_DEFAULT_COMPRESSION (-1/6) まで
- 保存アーカイブ:Z_BEST_COMPRESSION (9)
大きなファイルにはストリーム処理を使用する
同期メソッドの使用や大きなファイルの一括処理を避け、メモリ不足を防ぎます。
zlibインスタンスを再利用する
高頻度の圧縮操作では、zlibインスタンスを再利用できます:
例
const zlib = require('zlib');
const gzip = zlib.createGzip();
// 同じ gzip インスタンスを再利用して複数のファイルを処理
function compressFile(inputFile, outputFile) {
return new Promise((resolve, reject) => {
fs.createReadStream(inputFile)
.pipe(gzip)
.pipe(fs.createWriteStream(outputFile))
.on('finish', resolve)
.on('error', reject);
});
}
const gzip = zlib.createGzip();
// 同じ gzip インスタンスを再利用して複数のファイルを処理
function compressFile(inputFile, outputFile) {
return new Promise((resolve, reject) => {
fs.createReadStream(inputFile)
.pipe(gzip)
.pipe(fs.createWriteStream(outputFile))
.on('finish', resolve)
.on('error', reject);
});
}
エラーハンドリング
圧縮・解凍プロセス中のエラーを適切に処理します:
例
const zlib = require('zlib');
// ストリーム処理のエラーハンドリング
inputStream
.pipe(zlib.createGunzip())
.pipe(outputStream)
.on('error', (err) => {
console.error('解凍プロセス中にエラー:', err);
// リソースをクリーンアップ
});
// 非同期コールバックのエラーハンドリング
zlib.gzip(inputData, (err, compressed) => {
if (err) {
console.error('圧縮失敗:', err);
return;
}
// 圧縮データを処理
});
// ストリーム処理のエラーハンドリング
inputStream
.pipe(zlib.createGunzip())
.pipe(outputStream)
.on('error', (err) => {
console.error('解凍プロセス中にエラー:', err);
// リソースをクリーンアップ
});
// 非同期コールバックのエラーハンドリング
zlib.gzip(inputData, (err, compressed) => {
if (err) {
console.error('圧縮失敗:', err);
return;
}
// 圧縮データを処理
});
よくある質問と回答
圧縮後のデータが元のデータより大きい場合はどうすればよいですか?
この状況は通常、以下の場合に発生します:
- データ自体が既に圧縮されている(JPEG、MP3 など)
- データ量が非常に少ない(圧縮ヘッダー情報がデータ自体より大きい可能性がある)
- 不適切な圧縮レベルを使用している
解決策:
- データが既に圧縮されているか確認する
- 小さいデータには圧縮を無効にする
- 異なる圧縮レベルを試す
データが既に圧縮されているかどうかを判断するには?
ファイルヘッダーの確認や解凍を試みることで判断できます:
例
function isGzipped(buffer) {
return buffer.length >= 3 &&
buffer[0] === 0x1F &&
buffer[1] === 0x8B &&
buffer[2] === 0x08;
}
return buffer.length >= 3 &&
buffer[0] === 0x1F &&
buffer[1] === 0x8B &&
buffer[2] === 0x08;
}
圧縮データが破損した場合の対処方法は?
例
try {
const decompressed = zlib.inflateSync(compressedData);
} catch (err) {
if (err.code === 'Z_DATA_ERROR') {
console.error('データが破損しているか、形式が正しくありません');
} else {
console.error('解凍エラー:', err);
}
}
const decompressed = zlib.inflateSync(compressedData);
} catch (err) {
if (err.code === 'Z_DATA_ERROR') {
console.error('データが破損しているか、形式が正しくありません');
} else {
console.error('解凍エラー:', err);
}
}
まとめ
Node.js の zlib モジュールは強力なデータ圧縮・解凍機能を提供し、適切に使用することで以下のことが可能になります:
- ネットワーク転送データ量を大幅に削減
- 保存容量を節約
- アプリケーション全体のパフォーマンスを向上
重要なポイント:
- シーンに応じて同期・非同期・ストリーム処理を選択する
- 圧縮パラメータを適切に設定し、速度と圧縮率のバランスを取る
- エラーとエッジケースを適切に処理する
- 大きなファイルにはストリーム処理を使用してメモリ問題を回避する
zlib モジュールを習得することで、Node.js アプリケーションに効率的なデータ圧縮機能を追加し、リソース使用とユーザーエクスペリエンスを最適化できます。
その他の拡張