目次

画像をAPIに送るとき、JWTトークンを見るとき、メールの添付ファイルを扱うとき——Base64は至る所で使われている。「なぜわざわざテキストに変換するのか」という理由を理解すると、マルチモーダルAIのAPIの設計思想も見えてくる。

なぜバイナリをテキストに変換するのか

コンピュータのデータはすべてバイナリ(0と1の羅列)だが、テキストを前提に設計されたシステムはバイナリデータを正しく扱えないことがある。

問題の具体例:

  • メール(SMTP): 歴史的に7ビットASCIIテキストのみを扱う設計
  • JSON/HTTP ヘッダー: テキスト形式のため、バイナリを直接埋め込めない
  • URL: 使える文字に制限がある(特殊文字はエスケープ必要)
  • データベース: TEXT型のカラムにバイナリを直接格納できない

Base64はバイナリデータを「テキスト安全な文字列」に変換することでこれらの問題を解決する。

Base64の仕組み

Base64は64種類の文字(A-Z, a-z, 0-9, +, /)を使ってバイナリを表現する。

変換プロセス:

1. バイナリデータを6ビットのグループに分割
2. 各6ビット(0〜63)を対応する文字に変換
3. 4文字(=24ビット)が3バイトに対応
4. 余りが出たら = でパディング
# Pythonでの実装確認
import base64

# テキストをBase64エンコード
text = "Hello, AI!"
encoded = base64.b64encode(text.encode('utf-8'))
print(encoded)  # b'SGVsbG8sIEFJIQ=='

# デコード
decoded = base64.b64decode(encoded).decode('utf-8')
print(decoded)  # Hello, AI!

# ファイル(バイナリ)をBase64に
with open("image.png", "rb") as f:
    image_bytes = f.read()
    image_b64 = base64.b64encode(image_bytes).decode('utf-8')
    print(f"エンコード後: {len(image_b64)} 文字")
    print(f"元のサイズ: {len(image_bytes)} バイト")
    # Base64後は約33%大きくなる(4/3倍)

サイズのトレードオフ: Base64変換でデータサイズは約33%増加する。3バイトを4文字で表現するため(3×8=24ビット→4×6=24ビット、4文字)。

文字セットのバリアント

バリアント文字用途
標準 Base64A-Z a-z 0-9 + /メール、一般
URL安全 Base64A-Z a-z 0-9 - _URL、JWT
Base64(パディングなし)末尾の = を省略JWT など
# URL安全なBase64(+ → -, / → _)
url_safe = base64.urlsafe_b64encode(b"\xfb\xff\xfe")
print(url_safe)  # b'----' のような文字列(URLでも安全)

# パディングなし(JWTで使用)
no_pad = base64.urlsafe_b64encode(b"data").rstrip(b'=')

マルチモーダルAI APIでのBase64

GPT-4 Vision, Claude 3, Gemini などのマルチモーダルAIに画像を送る際、Base64が活躍する。

import base64
import anthropic
from pathlib import Path

def image_to_base64(image_path: str) -> tuple[str, str]:
    """画像ファイルをBase64文字列に変換"""
    path = Path(image_path)
    suffix_to_mediatype = {
        '.jpg': 'image/jpeg',
        '.jpeg': 'image/jpeg',
        '.png': 'image/png',
        '.gif': 'image/gif',
        '.webp': 'image/webp',
    }
    media_type = suffix_to_mediatype.get(path.suffix.lower(), 'image/png')
    with open(image_path, 'rb') as f:
        image_data = base64.standard_b64encode(f.read()).decode('utf-8')
    return image_data, media_type

# Claude APIに画像を送る
client = anthropic.Anthropic()
image_data, media_type = image_to_base64("chart.png")

message = client.messages.create(
    model="claude-opus-4-6",
    max_tokens=1024,
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "image",
                    "source": {
                        "type": "base64",
                        "media_type": media_type,
                        "data": image_data,
                    },
                },
                {
                    "type": "text",
                    "text": "このグラフを分析して日本語で説明してください。"
                }
            ],
        }
    ],
)
print(message.content[0].text)

URLとBase64の使い分け

# URL方式(画像がWebで公開されている場合)
messages = [{
    "role": "user",
    "content": [
        {
            "type": "image",
            "source": {
                "type": "url",
                "url": "https://example.com/chart.png"
            }
        },
        {"type": "text", "text": "説明してください"}
    ]
}]

# Base64方式(ローカルファイル・非公開画像)
messages = [{
    "role": "user",
    "content": [
        {
            "type": "image",
            "source": {
                "type": "base64",
                "media_type": "image/png",
                "data": image_b64_string
            }
        },
        {"type": "text", "text": "説明してください"}
    ]
}]

JWT(JSON Web Token)での使われ方

JWTは3つの部分をドット(.)で連結したもので、各部分がBase64URLエンコードされている。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiJ1c2VyXzEyMyIsImV4cCI6MTcxNzAwMDAwMH0.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
import base64
import json

def decode_jwt_payload(token: str) -> dict:
    """JWTのペイロード部分をデコード(署名検証なし・学習用)"""
    parts = token.split('.')
    payload_b64 = parts[1]

    # Base64URLのパディングを追加
    padding = 4 - len(payload_b64) % 4
    if padding != 4:
        payload_b64 += '=' * padding

    payload_bytes = base64.urlsafe_b64decode(payload_b64)
    return json.loads(payload_bytes)

# JWTをデコードして中身を確認
token = "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxMjMsInJvbGUiOiJhZG1pbiJ9.xxxxx"
payload = decode_jwt_payload(token)
print(payload)  # {'user_id': 123, 'role': 'admin'}

注意: JWTの内容は誰でもデコードできる(暗号化ではなくエンコード)。機密情報を含めてはいけない。

Data URL——HTMLに画像を埋め込む

Base64を使うと画像をHTML/CSS内に直接埋め込める。

<!-- 通常: 外部ファイル参照 -->
<img src="logo.png" alt="ロゴ">

<!-- Data URL: Base64埋め込み -->
<img src="data:image/png;base64,iVBORw0KGgoAAAANS..." alt="ロゴ">
def image_to_data_url(image_path: str) -> str:
    """画像をData URL形式に変換"""
    with open(image_path, 'rb') as f:
        data = base64.b64encode(f.read()).decode('utf-8')
    ext = image_path.rsplit('.', 1)[-1].lower()
    mime = {'jpg': 'image/jpeg', 'jpeg': 'image/jpeg', 'png': 'image/png'}.get(ext, 'image/png')
    return f"data:{mime};base64,{data}"

# HTMLメールやスタンドアロンHTMLに便利
data_url = image_to_data_url("icon.png")
html = f'<img src="{data_url}" alt="アイコン">'

まとめ

Base64は「バイナリデータをテキスト安全な形式に変換する」シンプルだが広く使われる手法だ。

  • なぜ必要か: メール・JSON・URLなどテキスト前提のシステムでバイナリを扱うため
  • 仕組み: 3バイト→4文字(64種類の文字使用)、サイズは約33%増加
  • バリアント: URL安全(JWT用)・パディングあり/なし
  • マルチモーダルAI: 画像をBase64でAPIに送るのが標準パターン
  • JWT: ヘッダー・ペイロードをBase64URLエンコード(暗号化ではない)
  • Data URL: HTMLに画像を直接埋め込む際に利用

「なぜこれが必要か」という背景を理解すると、Base64が登場する場面で迷わずに使えるようになる。

免責事項 — 掲載情報は執筆時点のものです。料金・機能は変更される場合があります。最新情報は各公式サイトをご確認ください。