FlowingDev

Base64画像:ピクセルをプレーンテキストに変えるアート

Base64エンコーディングが画像のバイナリデータをHTML、CSS、JSONに直接埋め込めるテキスト文字列に変換する仕組みと、それがなぜクールなトリックなのかを学びましょう。

ツールを試す: 画像 ↔ Base64

一言で言うと

画像のためのBase64とは、写真のバイナリデータをプレーンテキストの文字列にエンコードする方法です。これにより、別のファイルにリンクする代わりに、画像をコードに直接埋め込むことができます。

解決する問題

ウェブの初期の頃、物事はシンプルでした。HTMLファイルがあり、画像が欲しければ <img> タグを使って logo.gif のような別の画像ファイルを指し示しました。ブラウザはHTMLを読み、そのタグを見て、次に 2回目の 通信をサーバーに行い logo.gif を取得していました。1つのページに、2つのリクエストです。

さて、現代のウェブページを想像してみてください。ロゴ、ナビゲーション用の小さなアイコンが10数個、フッターにはソーシャルメディアのロゴ、そして背景パターンがあるかもしれません。これらがそれぞれ別のファイルだとしたら、もはや2つのリクエストの話ではありません。20、30、あるいはそれ以上になるでしょう!各リクエストは、ファイルがどんなに小さくてもオーバーヘッドがあります。それはまるで、同じ倉庫に小さな荷物を1つずつ取りに行くために、30台の小さな配達バンの船隊を送るようなものです。非効率的で、ユーザーにページが表示される速度を遅くします。

これこそが、Data URIとBase64エンコーディングが解決する核心的な問題、「リクエストが多すぎる」問題です。ブラウザに「あそこのアイコンを取ってきて」と指示する代わりに、「アイコンは ここ 、このCSSファイルの中にありますよ」と言えたらどうでしょう?

画像をテキストに変換してHTMLやCSSに直接配置することで、アセットをドキュメント自体にバンドルします。これにより、それらの画像のための余分なネットワークリクエストが不要になり、特に小さくて重要なグラフィックの場合、初期ページの読み込みがずっと速く感じられるようになります。これはトレードオフです。最初のHTML/CSSファイルは大きくなりますが、多くの小さな往復ネットワーク通信の時間とオーバーヘッドを節約できるのです。

内部の仕組み

では、どうやって美しく複雑な画像を、まるで猫がキーボードの上を歩いたような退屈なテキストブロックに変えるのでしょうか?これは2つのパートからなるプロセスです。画像をデータとして理解し、次にBase64エンコーディングスキームを適用します。

ピクセルからバイトへ

まず、「画像」という言葉を忘れてください。「ファイル」と考えてください。あなたのコンピュータ上にあるPNG、JPEG、GIFファイルは、魔法のような色の集まりではありません。それは高度に構造化されたバイトの連続、つまり1と0のストリームです。このバイナリデータには、メタデータ(画像の寸法など)、カラーパレット、そして圧縮されたピクセルデータ自体が含まれています。

問題は、このバイナリデータをHTMLやCSSのようなテキストファイルにそのままコピー&ペーストできないことです。テキストファイルにはルールがあります。特定のバイト値は「改行」や「ファイルの終わり」を意味したり、あるいは単に無効でコードを壊してしまったりします。画像の生のバイナリデータを、どんなシステムでも理解できる「安全な」文字セットだけを使って表現する方法が必要なのです。

Base64のマジックトリック

ここでBase64エンコーディングの出番です。その仕事は、あらゆるバイナリデータを、64個の一般的で安全に転送できるASCII文字だけを使って表現することです。その文字セットは A-Z、a-z、0-9、+、そして / です。それだけです。

そのプロセスは、巧妙なバイナリのジャグリングです。

  1. 3バイト読み込む: エンコーダーはバイナリ画像データを一度に3バイトずつ読み取ります。1バイトは8ビットなので、3 x 8 = 24ビットになります。
  2. 4つのチャンクに分割する: この24ビットの塊を、4つの6ビットのチャンクに再分割します(4 x 6 = 24ビット)。
  3. 文字にマッピングする: 各6ビットのチャンクは0(000000)から63(111111)までの数値を表現できます。この数値は、64文字のBase64アルファベットから文字を検索するためのインデックスとして使用されます。

原理は全く同じなので、簡単なテキストの例で見てみましょう。「cat」という単語をエンコードしてみます。

ステップ 説明 データ
1. 元のASCII 'c', 'a', 't' のASCII値。 99, 97, 116
2. 3バイト(24ビット)として 各文字の8ビットバイナリ。 01100011 01100001 01110100
3. 4 x 6ビットチャンクとして 24ビットを再グループ化。 011000 110110 000101 110100
4. 10進数の値 各6ビットチャンクの10進数値。 24, 54, 5, 52
5. Base64文字 Base64テーブルで各10進数を検索。 Y, 2, F, 0

というわけで、テキスト「cat」はBase64文字列「Y2F0」になります。

データが3バイトの倍数でない場合はどうなるでしょう?エンコーダーは、元のデータが完全には割り切れなかったことを示すために、末尾にパディング文字(=)を追加します。=が1つなら最後のグループが2バイトしかなかったこと、==なら1バイトしかなかったことを意味します。

このプロセスにより、データサイズは約33%増加します。なぜなら、元々3バイトのデータを表現するために4文字(4バイト)を使用しているからです。

Data URIラッパー

さて、巨大なBase64テキストの文字列ができました。ブラウザは、それがPNG画像であることを自動的には知りません。Data URIを使って、それが何であるかを伝えなければなりません。

Data URIには特定のフォーマットがあります。 data:[<MIME-type>][;base64],<data>

小さな赤い点のPNGの実際の例を分解してみましょう。

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==

  • data:: スキームです。これはブラウザに「データは他のURLではなく、まさにここにあります」と伝えます。
  • image/png: MIMEタイプです。これは非常に重要です。ブラウザに「今から渡すデータはPNG画像です。そのようにデコードしてください」と伝えます。これは image/jpeg や image/svg+xml などである可能性もあります。
  • ;base64: データがBase64エンコードされていることを示すオプションのフラグです。
  • ,: 区切り文字です。
  • iVBORw0K...: 実際のBase64エンコードされた画像データです。

ブラウザが <img> の src 属性やCSSの url() 関数でこの文字列を見ると、Base64文字列を元のバイナリバイトにデコードして画像をレンダリングします。これらすべてを、余分なネットワークリクエストを一切行うことなく実行するのです。

実例ストーリー

ガクガクするUIアイコンのケース

あるフロントエンド開発者、仮にプリヤとしましょう、彼女は洗練された新しいダッシュボードを構築していました。インターフェースには、小さくエレガントなSVGアイコンが満載でした。設定用の歯車、通知用のベル、検索用の虫眼鏡などです。彼女の高速なオフィスのWi-Fiでは、それは素晴らしく見えました。

しかし、シミュレートされた3G接続でテストしたとき、その体験は不快なものでした。ページのレイアウトとテキストは読み込まれますが、1、2秒の間、アイコンがあるべき場所に空白ができていました。そして、アイコンが一つずつポン、ポンと表示されるのです。安っぽく、壊れているように見えました。

問題は、15個のアイコンがそれぞれCSSファイル内の別々の background-image: url(...) であり、15回の個別のHTTPリクエストを引き起こしていたことでした。プリヤの解決策は、それぞれの小さなSVGをBase64表現に変換し、CSSに直接埋め込むことでした。

教訓: アイコンのような小さくて重要なUI要素には、それらをBase64としてCSSに埋め込むことで、レンダリングをブロックするネットワークリクエストをなくし、画像が欠けて「点滅」するのを防ぎ、よりスムーズでプロフェッショナルなユーザー体験を生み出すことができます。

自己完結型のプロジェクト提案書

コンサルタントのアレックスは、重要なクライアントにプロジェクト提案書を送る必要がありました。提案書はHTMLドキュメントで、PNG画像として生成されたいくつかのグラフと会社のロゴが含まれていました。彼は単にファイルのフォルダを送って、クライアントが正しくHTMLを開いてくれることを期待することはできませんでした。メールで添付ファイルを送るのも野暮ったく、一部のメールクライアントはデフォルトで外部画像をブロックします。

彼が必要としていたのは、単一の、誰でも確実に使えるファイルでした。彼はスクリプトを使い、最終的なHTMLと生成されたグラフ画像を取り込み、各画像をBase64エンコードし、<img src="chart1.png"> タグを <img src="data:image/png;base64,..."> に置き換えました。

結果としてできたのは、単一の、少しだけ大きいHTMLファイルでした。彼はこの1つのファイルをメールに添付でき、クライアントはオンラインでもオフラインでもそれを開き、すべてのグラフとロゴが完璧にレンダリングされた提案書を、何の疑問もなく見ることができました。

教訓: Base64は、ポータブルで自己完結型のドキュメントを作成するための素晴らしいツールです。メールや生成されたレポートのように、画像が「ただ動く」単一のファイルにバンドルする必要がある場合、それは完璧な解決策です。

よくある間違いと落とし穴

  • 巨大な画像に使うこと。 これは最大の禁じ手です。33%のサイズ増加を覚えていますか?2MBのヒーローイメージを2.66MBのテキストブロックに変えてHTMLに埋め込むのは、パフォーマンスの悪夢です。それはページのレンダリングをブロックし、ドキュメントサイズを膨張させ、ユーザーにとっては普通に画像を読み込むよりもはるかに遅くなります。小さな画像にのみ使用してください。
  • キャッシュのデメリットを無視すること。 別の画像ファイル(logo.png)は、最初の訪問後にブラウザによってキャッシュされます。そのロゴがサイトの100ページに表示される場合、ダウンロードされるのは一度だけです。もしそのロゴを100ページすべてのHTMLにBase64として埋め込んだ場合、ユーザーはその(より大きな)データを毎回再ダウンロードしなければなりません。
  • 完全なData URI構文を忘れること。 Base64文字列をそのままsrc属性に貼り付けることはできません。data:、MIMEタイプ(image/png、image/jpegなど)、そして;base64,プレフィックスを必ず含める必要があります。このコンテキストがなければ、ブラウザはその意味不明な文字列をどう扱っていいかわかりません。
  • CSSを読みにくくすること。 数十個の埋め込み画像を含むCSSファイルは、維持するのが悪夢になることがあります。ファイルが数千文字の文字列で肥大化し、スクロールして実際のスタイルルールを見つけるのが難しくなります。慎重に使い、プリプロセッサを使用している場合は、Base64文字列を(Sassの変数のように)別のファイルに保持することを検討してください。

アンテナを張っておくべき理由

小さくて重要で、すぐに表示される必要があるグラフィックを扱うときはいつでも、画像のBase64エンコーディングを検討すべきです。

  • ファーストビュー内のアイコン: 小さなロゴ、検索アイコン、または初期のユーザー体験に不可欠なメニュートグル。
  • CSSの背景パターン: 小さな繰り返しパターンで、余分なHTTPリクエストが大げさに感じられる場合。
  • 自己完結型ドキュメント: 外部の依存関係なしで単独で機能する必要がある単一のHTMLファイルを生成する場合(メール、レポート、オフラインドキュメント)。
  • APIレスポンス: APIがクライアントに2回目のリクエストを強制するのではなく、JSONペイロード内に直接小さなサムネイル画像を送信する方が効率的な場合があります。

これは特定の仕事のための特定のツールです。ファイルサイズとネットワークリクエスト数の間のトレードオフに勝つためのものです。賢く使えば、それは強力な最適化テクニックです。

もっと詳しく

理論はOK。さあ手を動かそう — 100%ブラウザ内で。

ツールを試す: 画像 ↔ Base64