Edited at

JavaScriptでBase64エンコード・デコード(UTF-8も)


Base64

様々なところで使われているエンコード方式です。バイナリデータから64進数にエンコードします。URLエンコードよりもデータ効率がいいらしい。


英文

マルチバイトでない文字列はbtoaatobを使ってエンコード・デコードできます。

const text = "Hello, world!";

// エンコード
const encoded = btoa(text);
//=> "SGVsbG8sIHdvcmxkIQ=="

// デコード
const decoded = atob(encoded);
//=> "Hello, world!"


日本語など

JavaScriptの文字列はUTF-16なので、そのままではエンコードできません。

btoa("テスト");

// Uncaught DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.

なので、URLエンコード等して全部1バイト文字

にするか

※この方法では、UTF-8を直接エンコードしたようなものはデコードできません

const text = "テスト";

// エンコード
const encoded = btoa(encodeURIComponent(text));
//=> "JUUzJTgzJTg2JUUzJTgyJUI5JUUzJTgzJTg4"

// デコード
const decoded = decodeURIComponent(atob(encoded));
//=> "テスト"

UTF-8などのバイト列に変換してからエンコードします。

TextEncoderでUTF-8にエンコードした結果のArrayBufferString.fromCharCodeに無理やり渡しているので、あまり良い方法ではないと思われます。

また、あまり長い文字列を渡すとエラーになるようです。1

const text = "テスト";

// エンコード
let utf8str = String.fromCharCode.apply(null, new TextEncoder().encode(text));
const encoded = btoa(utf8str);
//=> "44OG44K544OI"

// デコード
const decoded_utf8str = atob(encoded);
const decoded_array = new Uint8Array(Array.prototype.map.call(decoded_utf8str, c => c.charCodeAt()));
const decoded = new TextDecoder().decode(decoded_array);
//=> "テスト"

または、Blobを経由して変換する方法もあります。2

この場合は非同期になります。

const text = "テスト";

const encodedPromise = new Promise((resolve) => {
const reader = new FileReader();
reader.onload = () => {
const offset = reader.result.indexOf(",") + 1;
resolve(reader.result.slice(offset));
};
reader.readAsDataURL(new Blob([text]));
});
//=> Promise {<resolved>: "44OG44K544OI"}

const decodedPromise = encodedPromise
.then(encoded => fetch("data:text/plain;charset=UTF-8;base64," + encoded))
.then(response => response.text());
//=> Promise {<resolved>: "テスト"}