JavaScriptのbtoa()関数はASCII文字専用のため、日本語などのマルチバイト文字をそのまま渡すとエラーになります。
この記事では、encodeURIComponent・TextEncoder・Buffer.from(Node.js)など、日本語を安全にBase64エンコード・デコードする方法を網羅的に解説します。データURIスキームへの応用やファイルのBase64化まで、実務で使えるテクニックをすべてカバーしています。
この記事で学べること
- Base64エンコードとは何か(基本概念と用途)
btoa() / atob() の基本とASCII限定の問題
- encodeURIComponent + btoa で日本語をBase64化する方法
- TextEncoder / TextDecoder を使ったモダンな変換方法(推奨)
- Node.js での Buffer.from() を使った方法
- データURIスキームへの応用
- FileReader でファイルをBase64化する方法
- 汎用ユーティリティ関数の実装
- パフォーマンスとセキュリティの注意点
Base64エンコードとは
Base64は、バイナリデータを64種類のASCII文字(A-Z、a-z、0-9、+、/)とパディング文字(=)で表現するエンコード方式です。
| 項目 |
内容 |
| 使用文字 |
A-Z, a-z, 0-9, +, /(計64文字)+ パディング = |
| 変換単位 |
3バイト → 4文字(6ビットずつ区切る) |
| サイズ増加 |
元データの約1.33倍(33%増加) |
| 主な用途 |
メール添付(MIME)、データURI、API通信、JWT、Cookie |
ポイント:Base64は暗号化ではなく、エンコードです。誰でもデコードできるため、パスワードや機密情報の保護には使えません。
btoa() / atob() の基本
JavaScriptにはBase64エンコード・デコード用の組み込み関数が用意されています。
| 関数 |
役割 |
引数 |
戻り値 |
btoa() |
Binary to ASCII |
バイナリ文字列 |
Base64文字列 |
atob() |
ASCII to Binary |
Base64文字列 |
バイナリ文字列 |
ASCII文字のエンコード・デコード
btoa / atob の基本
// エンコード(文字列 → Base64)
const encoded = btoa("Hello World");
console.log(encoded); // "SGVsbG8gV29ybGQ="
// デコード(Base64 → 文字列)
const decoded = atob("SGVsbG8gV29ybGQ=");
console.log(decoded); // "Hello World"
実行結果
SGVsbG8gV29ybGQ=
Hello World
日本語でbtoa()がエラーになる理由
btoa()は各文字のコードポイントが0x00〜0xFF(Latin-1の範囲)の文字列のみ受け付けます。日本語などのマルチバイト文字はこの範囲を超えるため、InvalidCharacterErrorが発生します。
日本語でbtoa()を使うとエラー
try {
const result = btoa("こんにちは");
} catch (e) {
console.log(e.name); // "InvalidCharacterError"
console.log(e.message); // btoa に渡された文字列にLatin-1の範囲外の文字が含まれています
}
実行結果
InvalidCharacterError: Failed to execute 'btoa' on 'Window':
The string to be encoded contains characters outside of the Latin1 range.
注意:「あ」のコードポイントはU+3042(12354)で、Latin-1の上限0xFF(255)を大幅に超えています。そのためbtoa("あ")はエラーになります。
方法1: encodeURIComponent + btoa で日本語をBase64化
最もシンプルな方法は、encodeURIComponent()で日本語をパーセントエンコーディングに変換してからbtoa()に渡す方法です。
エンコード
encodeURIComponent + btoa
const text = "こんにちは世界";
// ステップ1: encodeURIComponent で日本語をパーセントエンコーディング
const percentEncoded = encodeURIComponent(text);
console.log(percentEncoded);
// "%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF%E4%B8%96%E7%95%8C"
// ステップ2: btoa() でBase64エンコード
const base64 = btoa(percentEncoded);
console.log(base64);
// "JUUzJTgxJTkzJUUzJTgyJTkzJUUzJTgxJUFCJUUzJTgxJUExJUUzJTgxJUFGJUU0JUI4JTk2JUU3JTk1JThD"
実行結果
%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF%E4%B8%96%E7%95%8C
JUUzJTgxJTkzJUUzJTgyJTkzJUUzJTgxJUFCJUUzJTgxJUExJUUzJTgxJUFGJUU0JUI4JTk2JUU3JTk1JThD
デコード
atob + decodeURIComponent
const base64Str = "JUUzJTgxJTkzJUUzJTgyJTkzJUUzJTgxJUFCJUUzJTgxJUExJUUzJTgxJUFGJUU0JUI4JTk2JUU3JTk1JThD";
// ステップ1: atob() でBase64デコード
const percentStr = atob(base64Str);
// ステップ2: decodeURIComponent でパーセントエンコーディングをデコード
const original = decodeURIComponent(percentStr);
console.log(original); // "こんにちは世界"
注意:この方法は手軽ですが、パーセントエンコーディングを経由するためBase64文字列が長くなるデメリットがあります。UTF-8バイト列を直接Base64化する次の方法が推奨です。
方法2: TextEncoder / TextDecoder を使った方法(推奨)
モダンブラウザで最も推奨される方法は、TextEncoderでUTF-8バイト列に変換し、各バイトをString.fromCharCode()でLatin-1文字列に変換してからbtoa()に渡す方法です。
エンコード(日本語 → Base64)
TextEncoder を使ったBase64エンコード(推奨)
function utf8ToBase64(str) {
// TextEncoder で UTF-8 バイト列(Uint8Array)に変換
const encoder = new TextEncoder();
const uint8Array = encoder.encode(str);
// 各バイトを Latin-1 文字に変換
const binaryStr = Array.from(uint8Array)
.map(byte => String.fromCharCode(byte))
.join("");
// btoa() で Base64 エンコード
return btoa(binaryStr);
}
const base64 = utf8ToBase64("こんにちは世界");
console.log(base64);
// "44GT44KT44Gr44Gh44Gv5LiW55WM"
実行結果
44GT44KT44Gr44Gh44Gv5LiW55WM
デコード(Base64 → 日本語)
TextDecoder を使ったBase64デコード
function base64ToUtf8(base64) {
// atob() で Base64 デコード → バイナリ文字列
const binaryStr = atob(base64);
// バイナリ文字列 → Uint8Array
const uint8Array = new Uint8Array(
[...binaryStr].map(char => char.charCodeAt(0))
);
// TextDecoder で UTF-8 バイト列を文字列に変換
const decoder = new TextDecoder();
return decoder.decode(uint8Array);
}
const original = base64ToUtf8("44GT44KT44Gr44Gh44Gv5LiW55WM");
console.log(original); // "こんにちは世界"
ポイント:この方法はencodeURIComponent方式と比べてBase64文字列が短くなります。UTF-8バイト列を直接変換するため、無駄なパーセントエンコーディングが不要だからです。
方法1と方法2の出力比較
同じ日本語文字列をエンコードした場合、出力サイズにどれだけ差があるか比較してみましょう。
エンコード結果の比較
const text = "こんにちは世界";
// 方法1: encodeURIComponent + btoa
const method1 = btoa(encodeURIComponent(text));
console.log(`方法1: ${method1}`);
console.log(`長さ: ${method1.length}文字`);
// 方法2: TextEncoder + btoa
const encoder = new TextEncoder();
const method2 = btoa(Array.from(encoder.encode(text)).map(b => String.fromCharCode(b)).join(""));
console.log(`方法2: ${method2}`);
console.log(`長さ: ${method2.length}文字`);
実行結果
方法1: JUUzJTgxJTkzJUUzJTgyJTkzJUUzJTgxJUFCJUUzJTgxJUExJUUzJTgxJUFGJUU0JUI4JTk2JUU3JTk1JThD
長さ: 84文字
方法2: 44GT44KT44Gr44Gh44Gv5LiW55WM
長さ: 28文字
| 方法 |
Base64の長さ |
特徴 |
| encodeURIComponent + btoa |
84文字 |
パーセントエンコーディング分が冗長 |
| TextEncoder + btoa(推奨) |
28文字 |
UTF-8バイト列を直接変換、効率的 |
方法3: Node.js での Buffer.from() を使った方法
Node.js環境ではBufferオブジェクトを使うことで、非常にシンプルにBase64変換が行えます。
エンコード
Node.js – Buffer.from() でエンコード
// Node.js 環境
const text = "こんにちは世界";
// エンコード: 文字列 → Base64
const base64 = Buffer.from(text, "utf-8").toString("base64");
console.log(base64);
// "44GT44KT44Gr44Gh44Gv5LiW55WM"
デコード
Node.js – Buffer.from() でデコード
const base64Str = "44GT44KT44Gr44Gh44Gv5LiW55WM";
// デコード: Base64 → 文字列
const original = Buffer.from(base64Str, "base64").toString("utf-8");
console.log(original); // "こんにちは世界"
実行結果
44GT44KT44Gr44Gh44Gv5LiW55WM
こんにちは世界
ポイント:Node.jsのBuffer.from()は内部でUTF-8エンコーディングを自動処理するため、TextEncoder方式と同じ結果が得られます。Node.js環境では最も簡潔な方法です。
URL安全なBase64(Base64URL)エンコード
標準のBase64は+と/を含むため、URLのクエリパラメータやファイル名に使うと問題が起きます。Base64URLはこれらを-と_に置換した形式です。
Base64URLエンコード / デコード
// Base64URL エンコード
function toBase64URL(str) {
const encoder = new TextEncoder();
const bytes = encoder.encode(str);
const binStr = Array.from(bytes).map(b => String.fromCharCode(b)).join("");
return btoa(binStr)
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, ""); // パディング除去
}
// Base64URL デコード
function fromBase64URL(base64url) {
let base64 = base64url
.replace(/-/g, "+")
.replace(/_/g, "/");
// パディング復元
while (base64.length % 4) base64 += "=";
const binStr = atob(base64);
const bytes = new Uint8Array([...binStr].map(c => c.charCodeAt(0)));
return new TextDecoder().decode(bytes);
}
// 使用例
const encoded = toBase64URL("日本語テスト+/=");
console.log(encoded); // URL安全な文字列
const decoded = fromBase64URL(encoded);
console.log(decoded); // "日本語テスト+/="
| 形式 |
使用文字 |
パディング |
用途 |
| 標準 Base64 |
A-Z, a-z, 0-9, +, / |
あり(=) |
メール、データURI |
| Base64URL |
A-Z, a-z, 0-9, –, _ |
なし |
JWT、URLパラメータ、ファイル名 |
データURIスキームへの応用
データURIは、外部ファイルを参照せずにデータをインラインで埋め込むための仕組みです。Base64エンコードしたデータをHTMLやCSSに直接記述できます。
テキストをデータURIに変換
テキストのデータURI化
function textToDataURI(text, mimeType = "text/plain") {
const encoder = new TextEncoder();
const bytes = encoder.encode(text);
const base64 = btoa(Array.from(bytes).map(b => String.fromCharCode(b)).join(""));
return `data:${mimeType};charset=utf-8;base64,${base64}`;
}
// 日本語HTMLをデータURIに変換
const html = "<h1>こんにちは</h1><p>日本語コンテンツ</p>";
const dataURI = textToDataURI(html, "text/html");
console.log(dataURI);
// iframeに表示する例
// document.querySelector("iframe").src = dataURI;
SVGをデータURIに変換
SVGのBase64データURI化
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<circle cx="50" cy="50" r="40" fill="#3b82f6"/>
<text x="50" y="55" text-anchor="middle" fill="white">日本語</text>
</svg>`;
const svgDataURI = textToDataURI(svg, "image/svg+xml");
// CSSの背景画像として使用
// element.style.backgroundImage = `url(${svgDataURI})`;
// img要素のsrcとして使用
// document.querySelector("img").src = svgDataURI;
ファイルをBase64化する方法(FileReader)
ブラウザで画像やPDFなどのファイルをBase64エンコードするには、FileReaderのreadAsDataURL()メソッドを使います。
基本的なファイルのBase64変換
FileReader でファイルをBase64化
// inputタグ: <input type="file" id="fileInput">
function fileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
// readAsDataURL の結果: "data:image/png;base64,iVBOR..."
const base64 = reader.result.split(",")[1]; // Base64部分のみ
resolve(base64);
};
reader.onerror = () => reject(reader.error);
reader.readAsDataURL(file);
});
}
// 使用例
document.getElementById("fileInput").addEventListener("change", async (e) => {
const file = e.target.files[0];
const base64 = await fileToBase64(file);
console.log(`ファイル名: ${file.name}`);
console.log(`サイズ: ${file.size} bytes`);
console.log(`Base64: ${base64.substring(0, 50)}...`);
});
画像をBase64に変換してプレビュー表示
画像のBase64プレビュー
function previewImage(file, imgElement) {
const reader = new FileReader();
reader.onload = () => {
// readAsDataURL はそのまま src に設定可能
imgElement.src = reader.result;
};
reader.readAsDataURL(file);
}
// 使用例
const input = document.getElementById("fileInput");
const preview = document.getElementById("preview");
input.addEventListener("change", (e) => {
const file = e.target.files[0];
if (file && file.type.startsWith("image/")) {
previewImage(file, preview);
}
});
Base64エンコード・デコードのユーティリティ関数
実務で使いやすいように、エンコード・デコード関数をまとめたユーティリティを紹介します。
Base64ユーティリティ(コピペで使えます)
const Base64Util = {
/** 文字列 → Base64(日本語対応) */
encode(str) {
const bytes = new TextEncoder().encode(str);
const binStr = Array.from(bytes, b => String.fromCharCode(b)).join("");
return btoa(binStr);
},
/** Base64 → 文字列(日本語対応) */
decode(base64) {
const binStr = atob(base64);
const bytes = new Uint8Array([...binStr].map(c => c.charCodeAt(0)));
return new TextDecoder().decode(bytes);
},
/** 文字列 → Base64URL(日本語対応) */
encodeURL(str) {
return this.encode(str)
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
},
/** Base64URL → 文字列(日本語対応) */
decodeURL(base64url) {
let base64 = base64url.replace(/-/g, "+").replace(/_/g, "/");
while (base64.length % 4) base64 += "=";
return this.decode(base64);
},
/** ファイル → Base64(Promise) */
fromFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result.split(",")[1]);
reader.onerror = () => reject(reader.error);
reader.readAsDataURL(file);
});
}
};
// 使用例
console.log(Base64Util.encode("日本語テスト")); // Base64文字列
console.log(Base64Util.decode("5pel5pys6Kqe44OG44K544OI")); // "日本語テスト"
console.log(Base64Util.encodeURL("URL安全+/テスト")); // Base64URL文字列
実行結果
5pel5pys6Kqe44OG44K544OI
日本語テスト
VVJM5a6J5YWoKy/jg4bjgrnjg4g
パフォーマンスと注意点
サイズ増加
Base64エンコードすると、データサイズは約33%増加します。大きなファイルのBase64化は慎重に検討してください。
サイズ増加の確認
function checkSizeIncrease(text) {
const encoder = new TextEncoder();
const originalBytes = encoder.encode(text).length;
const base64Str = Base64Util.encode(text);
const base64Bytes = base64Str.length;
console.log(`元のサイズ: ${originalBytes} bytes`);
console.log(`Base64サイズ: ${base64Bytes} bytes`);
console.log(`増加率: ${((base64Bytes / originalBytes - 1) * 100).toFixed(1)}%`);
}
checkSizeIncrease("Hello World"); // ASCII
checkSizeIncrease("こんにちは世界"); // 日本語
実行結果
元のサイズ: 11 bytes
Base64サイズ: 16 bytes
増加率: 45.5%
元のサイズ: 21 bytes
Base64サイズ: 28 bytes
増加率: 33.3%
セキュリティに関する注意
注意:Base64は暗号化ではありません。以下の点に注意してください。
- Base64でエンコードしたデータは誰でもデコードできます
- パスワードや個人情報の保護には暗号化(AES等)を使ってください
- APIキーやトークンをBase64で「隠す」のはセキュリティ対策になりません
- HTTP Basic認証のBase64は盗聴に対して無力です(HTTPS必須)
大きなデータの処理
大きなデータのチャンク処理
// 大きなBlob/FileをチャンクでBase64化(メモリ効率が良い)
async function largeFileToBase64(file, chunkSize = 1024 * 1024) {
const chunks = [];
let offset = 0;
while (offset < file.size) {
const slice = file.slice(offset, offset + chunkSize);
const arrayBuffer = await slice.arrayBuffer();
const bytes = new Uint8Array(arrayBuffer);
chunks.push(...bytes);
offset += chunkSize;
}
const binStr = chunks.map(b => String.fromCharCode(b)).join("");
return btoa(binStr);
}
ブラウザ互換性
| API |
Chrome |
Firefox |
Safari |
Edge |
Node.js |
btoa() / atob() |
1+ |
1+ |
3+ |
12+ |
16+ |
TextEncoder |
38+ |
19+ |
10.1+ |
79+ |
11+ |
TextDecoder |
38+ |
19+ |
10.1+ |
79+ |
11+ |
FileReader |
6+ |
3.6+ |
6+ |
12+ |
– |
Buffer.from() |
– |
– |
– |
– |
5.10+ |
ポイント:TextEncoder/TextDecoderはIE非対応ですが、モダンブラウザでは問題なく使用できます。IEサポートが必要な場合はencodeURIComponent方式を使ってください。
まとめ
JavaScriptで日本語をBase64エンコード・デコードする方法を紹介しました。最後に各方法の特徴を比較します。
| 方法 |
環境 |
効率 |
おすすめ度 |
| encodeURIComponent + btoa |
ブラウザ |
低(文字列が長い) |
レガシー対応時 |
| TextEncoder + btoa |
ブラウザ |
高 |
推奨 |
| Buffer.from() |
Node.js |
高 |
Node.jsでは最適 |
| FileReader |
ブラウザ |
– |
ファイル変換に特化 |
この記事のポイント
btoa()はASCII専用。日本語を直接渡すとInvalidCharacterErrorになる
- ブラウザではTextEncoder + btoaの組み合わせが最も効率的(推奨)
- Node.jsではBuffer.from(str, “utf-8”).toString(“base64”)が最もシンプル
- URLで使う場合はBase64URL形式(
+/= → -_)に変換する
- Base64はエンコードであり暗号化ではない。機密情報の保護には使えない
- Base64化するとデータサイズが約33%増加する点に注意