ガイド

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 を混ぜないでください。

標準とブラウザー資料