JavaScriptのbtoaとatobによるBase64エンコード・デコードとUnicode文字列の処理

Base64変換の基本API

ブラウザ環境では、データをBase64形式に変換・復元するための組み込み関数が提供されています。

  • window.btoa(): 8ビットのバイナリデータ(ASCII文字列)をBase64エンコードされた文字列に変換します。
  • window.atob(): Base64エンコードされた文字列をデコードし、元のバイナリデータ(ASCII文字列)を復元します。

これらのメソッドは、データの安全な転送や保存時にバイナリデータをテキスト形式として扱うために頻繁に利用されます。

基本的な使用例

const originalText = "Welcome to JavaScript";
const base64Encoded = window.btoa(originalText);
console.log(base64Encoded); // "V2VsY29tZSB0byBKYXZhU2NyaXB0"

const base64Decoded = window.atob(base64Encoded);
console.log(base64Decoded); // "Welcome to JavaScript"

通信プロトコルとBase64の必要性

特定のネットワークプロトコルやレガシーなシステムでは、ASCIIコードの0〜31に含まれる制御文字をそのまま送信するとエラーが発生する場合があります。このような制約を回避し、データを安全にペイロードに含めるために、送信前にBase64エンコードを適用し、受信側でデコードを行うという手法が一般的です。

マルチバイト文字(Unicode)の扱いとエラー回避

JavaScriptの文字列(DOMString)はUTF-16で内部的に表現されています。そのため、日本語や絵文字など、8ビット(1バイト)のASCII範囲を超えるUnicode文字を直接 window.btoa() に渡すと、InvalidCharacterError(Character Out Of Range)が発生します。

解決策1: URIエンコーディングとの組み合わせ

最も手軽な回避策は、Base64エンコード前に文字列をURIエンコードし、デコード時にURIデコードを行う方法です。

/**
 * Unicode文字列をBase64にエンコードする
 * @param {string} text - 変換対象の文字列
 * @returns {string} Base64エンコードされた文字列
 */
function encodeToBase64(text) {
  const uriEncoded = encodeURIComponent(text);
  const binaryString = uriEncoded.replace(/%([0-9A-F]{2})/g, (match, hex) => {
    return String.fromCharCode(parseInt(hex, 16));
  });
  return window.btoa(binaryString);
}

/**
 * Base64文字列をUnicode文字列にデコードする
 * @param {string} base64Str - Base64エンコードされた文字列
 * @returns {string} 復元されたUnicode文字列
 */
function decodeFromBase64(base64Str) {
  const binaryString = window.atob(base64Str);
  const uriEncoded = Array.from(binaryString)
    .map(char => '%' + ('00' + char.charCodeAt(0).toString(16)).slice(-2))
    .join('');
  return decodeURIComponent(uriEncoded);
}

const multibyteText = "こんにちは世界 🌍";
const encodedMultibyte = encodeToBase64(multibyteText);
console.log(encodedMultibyte); 

const decodedMultibyte = decodeFromBase64(encodedMultibyte);
console.log(decodedMultibyte); // "こんにちは世界 🌍"

解決策2: TextEncoderとTypedArrayの活用

よりモダンで堅牢なアプローチとして、TextEncoderTextDecoder APIを使用してUTF-8のバイト配列に変換し、それをBase64化する手法があります。大規模なデータやバイナリファイルを扱う場合は、base64-js のような専用ライブラリや、TypedArrayを直接処理するポリフィルを採用することが推奨されます。

インタラクティブな変換ツールの実装

以下は、ユーザーの入力に対してエンコードとデコードを実行するシンプルなUIコンポーネントの実装例です。インラインイベントハンドラを避け、モダンなイベントリスナーとエラーハンドリングを導入しています。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Base64変換ツール</title>
    <style>
        .result-box { margin-top: 10px; padding: 10px; border: 1px solid #ccc; min-height: 20px; word-break: break-all; }
        .error { color: #d32f2f; font-weight: bold; }
    </style>
</head>
<body>
    <h1>Base64エンコーダー / デコーダー</h1>
    <input type="text" id="userInput" placeholder="テキストを入力してください" style="width: 300px; padding: 5px;">
    <button id="btnEncode">エンコード</button>
    <button id="btnDecode">デコード</button>
    
    <h3>結果:</h3>
    <div id="outputArea" class="result-box"></div>

    <script>
        const inputField = document.getElementById('userInput');
        const outputArea = document.getElementById('outputArea');
        const btnEncode = document.getElementById('btnEncode');
        const btnDecode = document.getElementById('btnDecode');

        function handleEncode() {
            const rawText = inputField.value;
            if (!rawText) {
                showError('テキストが入力されていません。');
                return;
            }
            try {
                const result = encodeToBase64(rawText);
                displayResult(result);
            } catch (e) {
                showError('エンコード中にエラーが発生しました: ' + e.message);
            }
        }

        function handleDecode() {
            const base64Text = inputField.value;
            if (!base64Text) {
                showError('テキストが入力されていません。');
                return;
            }
            try {
                const result = decodeFromBase64(base64Text);
                displayResult(result);
            } catch (e) {
                showError('デコード中にエラーが発生しました。有効なBase64文字列を入力してください。');
            }
        }

        function displayResult(message) {
            outputArea.textContent = message;
            outputArea.className = 'result-box';
        }

        function showError(message) {
            outputArea.textContent = message;
            outputArea.className = 'result-box error';
        }

        btnEncode.addEventListener('click', handleEncode);
        btnDecode.addEventListener('click', handleDecode);

        // encodeToBase64 と decodeFromBase64 関数がスコープ内に定義されている前提
    </script>
</body>
</html>

タグ: javascript Base64 btoa atob WebAPI

9月12日 19:00 投稿