JavaScriptでBase64エンコード・デコードする方法

この記事ではJavaScriptで『Base64エンコードとデコードする方法』について、

  • btoa関数とatob関数を用いてBase64でエンコードとデコードする方法
  • btoa関数で日本語をBase64エンコードする方法
    • encodedURIComponent用いる方法
    • encodedURIComponentとunescape用いる方法
    • encodedメソッドでUint8Arrayに変換する方法
  • Node.jsでBase64エンコードとデコードを行う方法

などをプログラム例を用いて分かりやすく説明するように心掛けています。ご参考になれば幸いです。

btoa関数とatob関数を用いてBase64でエンコードとデコードする方法

JavaScriptにはBase64でエンコードとデコードする関数(btoa関数とatob関数)が標準で用意されています。

  • btoa関数
    • 文字列(string)をBase64形式のエンコード文字列(string)に変換する。
      • 引数:文字列(string)
      • 戻り値:エンコード文字列(string)
  • atob関数
    • Base64形式のエンコード文字列(string)を文字列(string)に変換する
      • 引数:エンコード文字列(string)
      • 戻り値:文字列(string)

以下にbtoa関数とatob関数を用いたプログラム例を示しています。

// エンコードしたい文字列
const originalString = 'ABCD';

// btoaを使用してBase64エンコード
const base64EncodedString = btoa(originalString);

console.log(base64EncodedString); // QUJDRA==

// atobを使用してBase64デコード
const base64DecodedString = atob(base64EncodedString);
console.log(base64DecodedString); // ABCD

上記のプログラムでは「ABCD」をbtoa関数を用いてBase64エンコードした後、atob関数を用いてBase64デコードしています。

btoa関数で日本語をBase64エンコードする方法(encodedURIComponent用いる)

btoa関数は日本語を直接エンコードすると、エラーが発生します(下記のエラーはGoogleのデベロッパーツールでのエラー結果です)。

btoa('テスト');
// エラーメッセージ
// Uncaught DOMException: Failed to execute 'btoa' on 'Window':
// The string to be encoded contains characters outside of the Latin1 range.

上記のエラーメッセージを見ると、「エンコードする文字列が Latin1 の範囲外の文字を含んでいます。」と記載されています。Base64エンコードするbtoa関数は、Latin1の範囲内の文字しかエンコードすることができません。例えば、日本語などのマルチバイト文字はLatin1の範囲外の文字になるので、btoa関数の引数に渡すとエラーになります。

Latin1で使える文字

Latin1で使える文字は以下のサイトで確認できます。
https://ja.wikipedia.org/wiki/ISO/IEC_8859-1

この場合、encodeURIComponent関数でURIエンコードすることで、Latin1範囲外の文字(以下のプログラム例では日本語の「あ」)をLatin1で使える文字に変換することができます。なお、URIエンコードした文字をデコードする時には、decodeURIComponent関数を用います。

// エンコードしたい文字列
const originalString = 'あ';

// encodeURIComponentを用いてURIエンコード
const uriEncodedString = encodeURIComponent(originalString);
console.log(uriEncodedString); // %E3%81%82

// btoaを使用してBase64エンコード
const base64EncodedString = btoa(uriEncodedString);
console.log(base64EncodedString); // JUUzJTgxJTgy

// atobを使用してBase64デコード
const base64DecodedString = atob(base64EncodedString);
console.log(base64DecodedString); // %E3%81%82

// decodeURIComponentを用いてデコード
const urlDecodedString = decodeURIComponent(base64DecodedString);
console.log(urlDecodedString); // あ

このプログラムでは、「あ」をencodeURIComponent関数を用いてURIエンコードして「%E3%81%82」に変換した後、btoa関数を用いてBase64エンコードしています。

encodeURIComponent関数の返り値は%XX(XXは16進数)という形式の文字列になります。「16進数の数値」も「%(U+0025)」もLatin1で使える文字なので、encodeURIComponent関数の返り値は必ずLatin1範囲内の文字列になることが保証されます。

ただし、この方法だとBase64エンコードの結果が異なります。

本来、「あ」をBase64エンコードすると、「44GC」となるが、上記のプログラムでは「JUUzJTgxJTgy」になっています。これは、Base64エンコードをするbtoa関数は引数として渡した文字列の文字1つ1つを1バイトのデータとみなすからです。「あ」をURIエンコードした結果の文字列「%E3%81%82」をbtoa関数の引数に渡しているので、Base64エンコードした結果が「JUUzJTgxJTgy」になっています。

次に、「あ」をBase64エンコードした結果が正しく「44GC」になる方法を説明します。

btoa関数で日本語をBase64エンコードする方法(encodedURIComponentとunescape用いる)

encodeURIComponent関数とdecodeURIComponent関数に加えて、unescape関数とescape関数を用いると、「あ」をBase64エンコードした結果が正しく「44GC」になります。encodeURIComponent関数でURIエンコードした後に、unescape関数を用いることで、正しく btoa関数で処理できる形式に変換することができるのです。

では実際のプログラム例を見てみましょう。

// エンコードしたい文字列
const originalString = 'あ';

// encodeURIComponentを用いてURIエンコード
const uriEncodedString = encodeURIComponent(originalString);
console.log(uriEncodedString); // %E3%81%82

// unescapeを用いてアンエスケープ(実際にはUTF-8のバイト列をLatin1文字列として取得)
const utf8ToLatin1String = unescape(uriEncodedString);
console.log(utf8ToLatin1String); // ã

// btoaを使用してBase64エンコード
const base64EncodedString = btoa(utf8ToLatin1String);
console.log(base64EncodedString); // 44GC

// atobを使用してBase64デコード
const base64DecodedString = atob(base64EncodedString);
console.log(base64DecodedString); // ã

// escapeを用いて再エスケープ(Latin1文字列をUTF-8のパーセントエンコードに変換)
const latin1StringToUtf8 = escape(base64DecodedString);
console.log(latin1StringToUtf8); // %E3%81%82

// decodeURIComponentを用いてデコード
const urlDecodedString = decodeURIComponent(latin1StringToUtf8);
console.log(urlDecodedString); // あ

unescape('%E3%81%82')をコンソール出力すると「ã」だけが表示されています。これは、E3が「ã」にデコードされ、「ã」に続く2バイトがLatin-1の非表示文字として解釈されるからです。

btoa関数で日本語をBase64エンコードする方法(encodedメソッドでUint8Arrayに変換する)

unescape関数とescape関数を使うと簡単にBase64エンコードとデコードすることができますが、これらの関数は現在、非推奨の関数になっています。

unescape関数とescape関数を用いずに、btoa関数で日本語をBase64エンコードするためには、以下の方法を用います。

  • 文字列をUint8Arrayに変換後、Latin1文字列にして、Base64エンコードする。
    • 文字列をUint8Arrayに変換するためには、TextEncoder.prototype.encodeを用いる。
  • Base64エンコードした結果を、Latin1文字列変換後、Uint8Arrayにして、文字列に変換する
    • Uint8Arrayを文字列に変換するためには、TextDecoder.prototype.decodeを用いる。

では実際のプログラム例を見てみましょう。

// エンコードしたい文字列
const originalString = 'あ';

// 文字列をTextEncoderを使用してUint8Arrayに変換
const encoder = new TextEncoder();
const uint8Arr = encoder.encode(originalString);
console.log(uint8Arr); // [ 227, 129, 130 ]

// Uint8ArrayをLatin1文字列に変換
const uint8ArrToLatin1String = Array.from(uint8Arr)
  .map((byte) => String.fromCharCode(byte))
  .join('');
console.log(uint8ArrToLatin1String); // ã

// btoaを使用してBase64エンコード
const base64EncodedString = btoa(uint8ArrToLatin1String);
console.log(base64EncodedString); // 44GC

// atobを使用してBase64デコード
const base64DecodedString = atob(base64EncodedString);
console.log(base64DecodedString); // ã

// Latin1文字列をUint8Arrayに変換
const latin1StringToUint8Arr = new Uint8Array(
  base64DecodedString.split('').map((char) => char.charCodeAt(0))
);
console.log(latin1StringToUint8Arr); // [ 227, 129, 130 ]

// Uint8Arrayを文字列にデコード
const decoder = new TextDecoder();
const decodedString = decoder.decode(latin1StringToUint8Arr);
console.log(decodedString); // あ

Node.jsでBase64エンコードとデコードを行う方法

Node.jsでBase64エンコードとデコードを行う場合、組み込みのBufferクラスを使用するのが一般的です。以下は、Node.jsを使用して日本語をBase64エンコードおよびデコードする方法を示すプログラム例です。

// エンコードしたい文字列
const originalString = 'あ';

// 文字列をBufferに変換
const buffer = Buffer.from(originalString);
console.log(buffer); // <Buffer e3 81 82>

// BufferからBase64エンコードされた文字列を取得
const base64EncodedString = buffer.toString('base64');
console.log(base64EncodedString); // 44GC

// Base64エンコードされた文字列をBufferに変換
const bufferFromBase64 = Buffer.from(base64EncodedString, 'base64');
console.log(bufferFromBase64); // <Buffer e3 81 82>

// Bufferからデコードされた文字列を取得
const decodedString = bufferFromBase64.toString();
console.log(decodedString); // あ

Bufferクラスは、多くの機能を持っています。

例えば、文字列やバイナリデータをBase64形式にエンコードしたり、Base64形式からデコードしたりする機能があります。これらの機能は、toString('base64')Buffer.from(string, 'base64')のようなメソッドを用います。

  • toString('base64')
    • バイナリデータ(Buffer)をBase64エンコードされた文字列に変換する。
  • Buffer.from(string, 'base64')
    • Base64エンコードされた文字列をバイナリデータ(バッファ)に変換(デコード)する。

本記事のまとめ

この記事ではJavaScriptで『Base64エンコードとデコードする方法』について、以下の内容を説明しました。

  • btoa関数とatob関数を用いてBase64でエンコードとデコードする方法
  • btoa関数で日本語をBase64エンコードする方法
    • encodedURIComponent用いる方法
    • encodedURIComponentとunescape用いる方法
    • encodedメソッドでUint8Arrayに変換する方法
  • Node.jsでBase64エンコードとデコードを行う方法

お読み頂きありがとうございました。