Base64 画像を圧縮する方法(Gzip が通常うまくいかない理由)
画像のデコード後に最適化して Base64 を小さくする方法、Gzip が Data URL の代わりにならない理由、JavaScript で100 KBを目指す手順を解説します。
Base64 が約33.3%大きくなる理由
Base64 は圧縮ではなく、3バイトを4文字にするエンコードです。正確な長さは 4 × ceil(n / 3) で、Data URL の接頭辞は別に加わります。
3つの処理経路を比べる
画像バイト→Base64 は標準ですが大きくなります。Base64→Gzip→Base64 は解凍が必要です。画像をデコード→最適化→Base64 なら、img src で使える標準画像を保てます。
Gzip の利点が再テキスト化で消えやすい理由
中間の Gzip は小さく見えても、再び Base64 にすると増分とヘッダーが加わります。結果は画像ではなく Gzip データなので、そのまま img src には置けません。
圧縮済み画像には余分な冗長性が少ない
JPEG、PNG、WebP はすでに形式ごとの圧縮を使います。実用的な削減は、画素数、非可逆品質、メタデータ、または形式を変えることで得ます。
JavaScript で正しく処理する
Base64 とシグネチャを検証し、ImageBitmap を作り、制限付き Canvas に描画して toBlob で再エンコードします。実運用では容量・画素上限、アニメーション拒否、WebP 検出、古いタスクの無効化も必要です。
ブラウザー標準 API の短い例
async function compressBase64Image(dataUrl, maxWidth = 1600, quality = 0.82) {
const [header, payload] = dataUrl.split(',', 2);
const mime = header.match(/^data:([^;]+);base64$/)?.[1];
if (!mime || !/^image\/(png|jpeg|webp)$/.test(mime)) throw new Error('Unsupported image');
const binary = atob(payload.replace(/\s+/g, ''));
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
const bitmap = await createImageBitmap(new Blob([bytes], { type: mime }));
try {
const scale = Math.min(1, maxWidth / bitmap.width);
const canvas = document.createElement('canvas');
canvas.width = Math.max(1, Math.round(bitmap.width * scale));
canvas.height = Math.max(1, Math.round(bitmap.height * scale));
const context = canvas.getContext('2d');
if (!context) throw new Error('Canvas 2D unavailable');
context.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
const blob = await new Promise((resolve) => canvas.toBlob(resolve, 'image/webp', quality));
if (!blob) throw new Error('Encoding failed');
const output = new Uint8Array(await blob.arrayBuffer());
let outputBinary = '';
for (let offset = 0; offset < output.length; offset += 0x8000) {
outputBinary += String.fromCharCode(...output.subarray(offset, offset + 0x8000));
}
return `data:${blob.type};base64,${btoa(outputBinary)}`;
} finally {
bitmap.close();
}
}
100 KBを目指す方法
100 KB は102,400画像バイトとして扱います。JPEG/WebP の品質を有限回二分探索し、届かなければ sqrt(目標/現在) で縦横比を保って縮小します。
品質・寸法・形式の違い
品質は同じ画素の符号化を変え、リサイズは画素自体を減らします。写真は JPEG/WebP、透明画像や図形は PNG/WebP が適することが多く、実測比較が必要です。
透明 PNG と JPEG 背景
JPEG には alpha がありません。透明画像を JPEG にするなら、PNG/WebP を維持するか、背景色を明示してから平坦化します。
GIF、WebP アニメ、SVG の扱い
Canvas は通常1フレームだけを扱います。GIF、アニメ WebP、複数画像 ICO は拒否し、SVG は専用のテキスト最適化を使うべきです。
ローカル処理のプライバシー
アップロードとサーバー保存を避けられますが、安全性を保証する検査ではありません。端末、拡張機能、ブラウザー codec のリスクは残ります。
Base64 をやめる判断
大きな画像は Blob URL、別ファイル、Blob/FormData/ArrayBuffer のバイナリ送信の方がキャッシュと転送に向きます。
よくある質問
Base64 自体を圧縮できますか?
できますが別形式になり、標準画像 Base64 として直接使えません。
img src に直接使えますか?
デコード結果が画像なら使えます。Gzip は先に解凍が必要です。
100 KBにする方法は?
品質を有限回探索し、必要なら縦横比を保って縮小します。
画質は下がりますか?
リサイズと非可逆品質では下がる可能性があります。
なぜ約33%増えますか?
3バイトを4文字にするためです。
アップロードされますか?
本站の圧縮器はブラウザー内だけで処理します。
Gzip は前と後のどちら?
通信では外側の HTTP 圧縮を使い、画像 Data URL に Gzip を混ぜないでください。