JavaScript/Compression Living Standard
Compression Living Standard
はじめに
Web開発の世界では、データの圧縮と展開は重要な役割を果たしています。特にクライアントサイドでのデータ処理能力が向上するにつれて、ブラウザ内でのデータ圧縮・展開処理の需要が高まっています。Compression Living Standardは、Webブラウザでのデータ圧縮と展開を標準化し、開発者がJavaScriptを使って効率的にデータを処理できるようにする仕様です。
Compression Streamsの基本
Compression Living Standardの中核となるのが、CompressionStream
とDecompressionStream
という2つのクラスです。これらは、ストリーミング処理を通じてデータを圧縮・展開することができます。
// 圧縮ストリームの作成 const compressedStream = new CompressionStream('gzip'); // 展開ストリームの作成 const decompressedStream = new DecompressionStream('gzip');
これらのクラスはTransformStream
インターフェースを実装しており、Streams APIと完全に連携することができます。
対応フォーマット
現在の仕様では、以下の圧縮フォーマットがサポートされています:
フォーマット | 説明 | 用途 |
---|---|---|
gzip |
広く使用されている圧縮形式 | 汎用的なデータ圧縮、HTTPレスポンスの圧縮 |
deflate |
zlib圧縮アルゴリズム | レガシーシステムとの互換性 |
deflate-raw |
ヘッダーとフッターのないdeflate | 特定の状況での効率的な圧縮 |
実践的な使用例
テキストデータの圧縮と展開
以下の例では、テキストデータを圧縮し、その後展開する方法を示しています:
async function compressAndDecompress(text) { // テキストをUint8Arrayに変換 const encoder = new TextEncoder(); const data = encoder.encode(text); // データを圧縮 const compressedStream = new CompressionStream('gzip'); const writer = compressedStream.writable.getWriter(); writer.write(data); writer.close(); // 圧縮されたデータを収集 const reader = compressedStream.readable.getReader(); const chunks = []; for (;;) { const { done, value } = await reader.read(); if (done) break; chunks.push(value); } // 圧縮されたデータをマージ const compressedData = new Uint8Array( chunks.reduce((acc, chunk) => acc + chunk.length, 0) ); let offset = 0; for (const chunk of chunks) { compressedData.set(chunk, offset); offset += chunk.length; } console.log(`元のサイズ: ${data.length} バイト`); console.log(`圧縮後のサイズ: ${compressedData.length} バイト`); // 圧縮されたデータを展開 const decompressedStream = new DecompressionStream('gzip'); const decompressWriter = decompressedStream.writable.getWriter(); decompressWriter.write(compressedData); decompressWriter.close(); // 展開されたデータを収集 const decompressReader = decompressedStream.readable.getReader(); const decompressedChunks = []; for (;;) { const { done, value } = await decompressReader.read(); if (done) break; decompressedChunks.push(value); } // 展開されたデータをマージして返却 const decompressedData = new Uint8Array( decompressedChunks.reduce((acc, chunk) => acc + chunk.length, 0) ); let decompressOffset = 0; for (const chunk of decompressedChunks) { decompressedData.set(chunk, decompressOffset); decompressOffset += chunk.length; } // テキストに戻す const decoder = new TextDecoder(); return decoder.decode(decompressedData); } // 使用例 const originalText = "これはCompression APIのテストです。同じデータが繰り返されると圧縮率が高くなります。" + "これはCompression APIのテストです。同じデータが繰り返されると圧縮率が高くなります。" + "これはCompression APIのテストです。同じデータが繰り返されると圧縮率が高くなります。"; compressAndDecompress(originalText).then(result => { console.log("元のテキストと一致:", result === originalText); });
ファイルの圧縮
以下の例では、ユーザーがアップロードしたファイルをブラウザ側で圧縮する方法を示しています:
document.getElementById('fileInput').addEventListener('change', async (event) => { const file = event.target.files[0]; if (!file) return; // ファイルをArrayBufferとして読み込む const arrayBuffer = await file.arrayBuffer(); const fileData = new Uint8Array(arrayBuffer); // データを圧縮 const compressedStream = new CompressionStream('gzip'); const compressWriter = compressedStream.writable.getWriter(); compressWriter.write(fileData); compressWriter.close(); // 圧縮されたデータを取得 const reader = compressedStream.readable.getReader(); const chunks = []; for (;;) { const { done, value } = await reader.read(); if (done) break; chunks.push(value); } // 圧縮されたデータを結合 let totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0); const compressedData = new Uint8Array(totalLength); let offset = 0; for (const chunk of chunks) { compressedData.set(chunk, offset); offset += chunk.length; } // 圧縮されたファイルを保存 const compressedBlob = new Blob([compressedData], { type: 'application/gzip' }); const downloadLink = document.createElement('a'); downloadLink.href = URL.createObjectURL(compressedBlob); downloadLink.download = file.name + '.gz'; downloadLink.click(); // 圧縮率を表示 const compressionRatio = ((1 - compressedData.length / fileData.length) * 100).toFixed(2); document.getElementById('compressionInfo').textContent = `元のサイズ: ${fileData.length} バイト, 圧縮後: ${compressedData.length} バイト (${compressionRatio}% 削減)`; });
対応するHTML:
<input type="file" id="fileInput"> <div id="compressionInfo"></div>
フェッチAPIとの統合
Compression APIはフェッチAPIと組み合わせることで、ネットワーク経由で受け取ったデータをクライアント側で展開することができます:
async function fetchAndDecompress(url) { // gzip圧縮データを取得 const response = await fetch(url); const compressedData = await response.arrayBuffer(); // データを展開 const decompressStream = new DecompressionStream('gzip'); const decompressedStream = new Response( new Blob([compressedData]).stream().pipeThrough(decompressStream) ).body; // 展開されたデータを読み込む const reader = decompressedStream.getReader(); const chunks = []; for (;;) { const { done, value } = await reader.read(); if (done) break; chunks.push(value); } // 展開されたデータを結合 const decompressedData = new Uint8Array( chunks.reduce((acc, chunk) => acc + chunk.length, 0) ); let offset = 0; for (const chunk of chunks) { decompressedData.set(chunk, offset); offset += chunk.length; } return decompressedData; } // 使用例 fetchAndDecompress('https://example.com/data.gz') .then(data => { // 展開されたデータを処理 const decoder = new TextDecoder(); const text = decoder.decode(data); console.log('展開されたデータ:', text); }) .catch(error => { console.error('エラー:', error); });
ブラウザ互換性と対応状況
現在のWebブラウザにおけるCompression Standard対応状況は、主要なブラウザでサポートが広がっています:
ブラウザ | サポート開始 |
---|---|
Chrome | 80+ |
Firefox | 102+ |
Safari | 15+ |
Edge | 80+ |
エラー処理とベストプラクティス
圧縮・展開処理を実装する際には、適切なエラー処理が重要です:
async function safeCompression(data, format = 'gzip') { try { const compressStream = new CompressionStream(format); const writer = compressStream.writable.getWriter(); writer.write(data); writer.close(); const reader = compressStream.readable.getReader(); const chunks = []; for (;;) { const { done, value } = await reader.read(); if (done) break; chunks.push(value); } // 結果を結合 let totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0); const result = new Uint8Array(totalLength); let offset = 0; for (const chunk of chunks) { result.set(chunk, offset); offset += chunk.length; } return { success: true, data: result, originalSize: data.length, compressedSize: result.length, ratio: ((1 - result.length / data.length) * 100).toFixed(2) + '%' }; } catch (error) { console.error(`圧縮エラー (${format}):`, error); return { success: false, error: error.message, format: format }; } }
パフォーマンスの考慮点
圧縮処理は計算負荷の高い操作です。以下の点に注意することで、効率的に利用できます:
// 大きなファイルを処理する場合のチャンク処理の例 async function compressLargeFile(file, chunkSize = 1024 * 1024) { const fileSize = file.size; const compressStream = new CompressionStream('gzip'); const writer = compressStream.writable.getWriter(); // ファイルを小さなチャンクに分割して処理 for (let position = 0; position < fileSize; position += chunkSize) { const chunk = file.slice(position, position + chunkSize); const arrayBuffer = await chunk.arrayBuffer(); writer.write(new Uint8Array(arrayBuffer)); // 進捗表示(オプション) const progress = Math.min(100, Math.round((position + chunkSize) / fileSize * 100)); console.log(`処理中: ${progress}%`); } // ストリームを閉じる writer.close(); // 圧縮されたデータを収集 const reader = compressStream.readable.getReader(); const chunks = []; for (;;) { const { done, value } = await reader.read(); if (done) break; chunks.push(value); } // 結果を結合 const compressedSize = chunks.reduce((acc, chunk) => acc + chunk.length, 0); const compressedData = new Uint8Array(compressedSize); let offset = 0; for (const chunk of chunks) { compressedData.set(chunk, offset); offset += chunk.length; } return new Blob([compressedData], { type: 'application/gzip' }); }
まとめ
Compression Living Standardは、Webアプリケーションにおけるデータ圧縮・展開処理をシンプルかつ効率的に行うための強力なAPIです。ストリームベースの設計により、大きなデータセットであっても効率的に処理することができ、モダンなWeb開発において重要な役割を果たしています。この標準を活用することで、データ転送量の削減、処理速度の向上、そしてユーザーエクスペリエンスの改善を実現できます。
カテゴリ:JavaScript