Base64
様々なところで使われているエンコード方式です。バイナリデータから64進数にエンコードします。URLエンコードよりもデータ効率がいいらしい。
エンコード前 | URLエンコード | Base64エンコード |
---|---|---|
https://qiita.com/ (18) |
https%3A%2F%2Fqiita.com%2F (26) |
aHR0cHM6Ly9xaWl0YS5jb20v (24) |
こんにちは (15) |
%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF (45) |
44GT44KT44Gr44Gh44Gv (20) |
3バイトずつ4文字に変換します。
英文
マルチバイトでない文字列はbtoa
、atob
を使ってエンコード・デコードできます。
const text = "Hello, world!";
// エンコード
const encoded = btoa(text);
//=> "SGVsbG8sIHdvcmxkIQ=="
// デコード
const decoded = atob(encoded);
//=> "Hello, world!"
日本語など
JavaScriptの文字列はUTF-16なので、そのままではエンコードできません。
Base64はあくまでもバイト列のエンコードなので、バイト列に直してやる必要があります。
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にエンコードした結果のArrayBuffer
をString.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>: "テスト"}
Base64変換関数
ご利用はご自由にどうぞ。
function base64Encode(...parts) {
return new Promise(resolve => {
const reader = new FileReader();
reader.onload = () => {
const offset = reader.result.indexOf(",") + 1;
resolve(reader.result.slice(offset));
};
reader.readAsDataURL(new Blob(parts));
});
}
function base64Decode(text, charset) {
return fetch(`data:text/plain;charset=${charset};base64,` + text).then(response => response.text());
}
function base64DecodeAsBlob(text, type = "text/plain;charset=UTF-8") {
return fetch(`data:${type};base64,` + text).then(response => response.blob());
}
つかいかた
// Promise#then
base64Encode("こんにちは").then(encoded => { /* ... */ });
// await
const encoded = await base64Encode("こんにちは");
// ArrayBufferやBlobも使えます
const encodedFile = await base64Encode(inputFile.files[0]);
const decodedFile = await base64DecodeAsBlob(encodedFile, "image/jpeg");