ファイルを選択
  1. base64hub
  2. Base64 を学ぶ
  3. Uint8Array.toBase64()
ガイド

JavaScript の Uint8Array.toBase64 と fromBase64

JavaScript にようやく、バイト列を扱い、Base64URL に対応し、デコードの厳格さを選べる Base64 API が加わりました。btoa、atob、手書きのループの用途のほとんどを置き換えます。

1 行でエンコードとデコード

この API は、Base64 用に 3 つ、16進数用に 3 つのメソッドを追加します。

メソッド機能
bytes.toBase64(options)Uint8Array を Base64 文字列に
Uint8Array.fromBase64(string, options)Base64 文字列を新しい Uint8Array に
bytes.setFromBase64(string, options)既存の Uint8Array にデコード
bytes.toHex()Uint8Array を小文字の 16進数文字列に
Uint8Array.fromHex(string)16進数文字列を新しい Uint8Array に
bytes.setFromHex(string)既存の Uint8Array に 16進数をデコード

テキストは先にバイト列にする必要があります。TextEncoder は常に UTF-8 を出力するので、Unicode も小細工なしで扱えます。

const bytes = new TextEncoder().encode("Grüße, 世界");
const b64 = bytes.toBase64();
console.log(b64); // "R3LDvMOfZSwg5LiW55WM"

const back = new TextDecoder().decode(Uint8Array.fromBase64(b64));
console.log(back); // "Grüße, 世界"

fetch().arrayBuffer() や crypto.subtle.digest() などから得た ArrayBuffer には、ビューが必要です:new Uint8Array(buffer).toBase64()。その他の型付き配列にはメソッドそのものがありません。Uint16Array の生のメモリをエンコードするには、new Uint8Array(u16.buffer, u16.byteOffset, u16.byteLength) のように包みます。

オプション:alphabet、omitPadding、lastChunkHandling

alphabet

"base64"(デフォルト)は + と / を使い、"base64url" は - と _ を使います。このオプションはエンコードとデコードの両方に適用され、一方のアルファベットに設定したデコーダーは、もう一方のアルファベットの文字を拒否します。

const data = new Uint8Array([251, 255]);
data.toBase64();                                         // "+/8="
data.toBase64({ alphabet: "base64url" });                // "-_8="
data.toBase64({ alphabet: "base64url", omitPadding: true }); // "-_8"

Uint8Array.fromBase64("-_8", { alphabet: "base64url" }); // Uint8Array [251, 255]
Uint8Array.fromBase64("-_8");                            // SyntaxError

omitPadding

toBase64 は、Base64URL のアルファベットでもデフォルトで = のパディングを付けます。JWT 形式の出力が必要なら omitPadding: true を渡してください。デコード時のパディングは lastChunkHandling で制御します。

lastChunkHandling

このオプションは、fromBase64 と setFromBase64 が最後の文字のグループをどう扱うかを決めます。Hi?> のバイト列にデコードされる入力で 3 つのモードを試した結果です。

入力"loose"(デフォルト)"strict""stop-before-partial"
SGk/Pg==Hi?>Hi?>Hi?>
SGk/Pg(パディングなし)Hi?>SyntaxErrorHi?(Pg の手前で停止)
SGk/Ph==(パディングのビットがゼロでない)Hi?>SyntaxErrorHi?>
SGk/P(1 文字だけ余る)SyntaxErrorSyntaxErrorHi?
  • loose は atob と同じように振る舞います。パディングは任意で、最後の文字の余ったビットは無視されます。
  • strict は、RFC 4648 が定める正規でパディング付きの入力だけを受け付けます。Base64 文字列を比較したり署名したりする場合など、エンコード後の形が一意でなければならないときに使います。
  • stop-before-partial は、完全な 4 文字のグループをすべてデコードし、不完全な末尾には手を付けません。グループの残りが次のチャンクで届くストリーミング処理のためのモードです。

どのモードでも ASCII の空白(スペース、タブ、改行)は読み飛ばされるので、MIME 形式で折り返された入力もそのままデコードできます。それ以外のアルファベット外の文字は SyntaxError になります。

既存のバッファーに書き込む

setFromBase64 と setFromHex は、手元にある Uint8Array にデコードし、どれだけ使ったかを返します。

const target = new Uint8Array(8);
const result = target.setFromBase64("SGVsbG8gd29ybGQ=");
console.log(result); // { read: 8, written: 6 }

Hello world は 11 バイトで、書き込み先は 8 バイトです。デコーダーは収まる 3 バイト単位のグループしか書き込まないので、6 バイトで止まり、8 文字を読んだと報告します。続けるには、文字列の残りとターゲットの subarray を渡してもう一度呼び出します。stop-before-partial と組み合わせれば、ストリームをチャンクごとに、あらかじめ確保したバッファーへデコードできます。

const out = new Uint8Array(1024);
const { read, written } = out.setFromBase64("SGk/Pg", {
  lastChunkHandling: "stop-before-partial",
});
// read: 4, written: 3. Keep "Pg" and prepend it to the next chunk.

16進数のメソッドも同じパターンです。toHex() は常に小文字で書き、fromHex() は大文字も小文字も受け付け、桁数が奇数だと SyntaxError になります。

new Uint8Array([0xde, 0xad, 0xbe, 0xef]).toHex(); // "deadbeef"
Uint8Array.fromHex("CAFE");                       // Uint8Array [202, 254]

btoa と atob が扱いにくい理由

btoa と atob はブラウザの初期の時代からあるもので、「バイナリ文字列」、つまりすべての文字コードが 0〜255 のバイト値である JavaScript の文字列を扱います。これが 3 つの問題を引き起こします。

Unicode は例外になるか、黙っておかしくなる。 U+00FF より大きい文字があると btoa は InvalidCharacterError を投げます(Chrome では「The string to be encoded contains characters outside of the Latin1 range」)。U+0080 から U+00FF までの文字はさらにたちが悪く、エラーなしに Latin-1 としてエンコードされます。btoa("Grüße") は R3L832U= を返しますが、他のあらゆるシステムが期待する UTF-8 でのエンコード結果は R3LDvMOfZQ== です。

デコード結果がバイト列ではなく文字列になる。 atob("Q2Fmw6k=") は "Café" を返します。本来のテキストを得るには、文字列をバイト列に変換してから UTF-8 としてデコードする必要があり、ループか Uint8Array.from(atob(s), c => c.charCodeAt(0)) が要ります。

大きな入力には注意が必要。 よく使われるワンライナー btoa(String.fromCharCode(...bytes)) はすべてのバイトを関数の引数として渡すため、1 MB の配列では RangeError: Maximum call stack size exceeded が発生します。文字列はチャンクに分けて組み立てる必要があります。

さらに、Base64URL への対応も、パディングや厳格さの制御もありません。昔からある回避策 btoa(unescape(encodeURIComponent(text))) は確かに UTF-8 の Base64 を出力しますが、非推奨の unescape に頼っています。新しい API なら、これらはすべて new TextEncoder().encode(text).toBase64() で済みます。

ブラウザと Node.js の対応状況

これらのメソッドは TC39 の提案「Uint8Array to/from Base64」に由来し、2025 年 7 月にステージ 4 に到達して ECMAScript 2026 に含まれています。

ランタイム6 つのメソッドすべてを備えた最初のバージョン
Chrome、Edge140
Firefox133
Safari(macOS と iOS)18.2
Node.js25.0.0

Chrome 140 によって、この機能は 2025 年 9 月に Baseline になり、現行の主要ブラウザはすべて対応しています。Node.js では、メソッドを直接テストしました。Node 25.0.0 と 26.12.1 は 6 つすべてを備えています。LTS リリースの Node 24.22.1 は、デフォルトではどれも備えていません。V8 は実験的な --js-base-64 フラグを受け付けますが、私たちのテストでは、このフラグで使えるようになったのは toBase64、fromBase64、toHex、fromHex で、setFromBase64 と setFromHex はありませんでした。Node 24 は非対応とみなしてください。Node 24 以前では Buffer.from(b64, "base64") と buf.toString("base64") を使います。こちらも "base64url" を受け付けます。MDN の互換性データによれば、Deno 2.5 と Bun 1.1.22 もこの API に対応しています。

古いランタイム向けのフォールバック

機能を検出し、なければチャンク分割した btoa/atob にフォールバックします。このコードは Node 24(フォールバック側)と Node 26(ネイティブ側)で、UTF-8 テキストと 1 MB の配列を使ってテストしました。

function bytesToBase64(bytes) {
  if (typeof bytes.toBase64 === "function") return bytes.toBase64();
  let bin = "";
  for (let i = 0; i < bytes.length; i += 0x8000) {
    bin += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
  }
  return btoa(bin);
}

function base64ToBytes(b64) {
  if (typeof Uint8Array.fromBase64 === "function") return Uint8Array.fromBase64(b64);
  return Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
}

このフォールバックはデフォルトのオプションにしか対応していません。base64url、strict、setFromBase64 をどこでも使いたいなら、完全なポリフィルを使ってください。core-js は es.uint8-array.to-base64、es.uint8-array.from-base64 などのモジュールとして提供しています。

コードを書かずにエンコードを試すなら、Base64 エンコーダーとデコーダー、URLセーフなアルファベットなら Base64URL をお使いください。Node の Buffer を含む JavaScript の例は、JavaScript での Base64 と Node.js での Base64 にあります。

よくある質問

新しい Base64 API は今すぐ本番で使えますか?

ブラウザなら使えます。Chrome と Edge 140、Firefox 133、Safari 18.2 で搭載されています。Node.js ではバージョン 25 以降が必要です。古いブラウザや Node 24 以前をサポートするなら、フォールバックを用意してください。

Uint8Array.fromBase64 は Base64URL を受け付けますか?

はい、{ alphabet: "base64url" } を指定すれば受け付けます。このオプションがないと、- と _ で SyntaxError になります。デフォルトの loose モードでは、どちらのアルファベットでもパディングは任意です。

toBase64 で文字列をエンコードするにはどうすればいいですか?

先に UTF-8 のバイト列にします:new TextEncoder().encode(text).toBase64()。デコードは逆に new TextDecoder().decode(Uint8Array.fromBase64(b64)) とします。

Buffer や Uint16Array で toBase64 が関数でないと言われるのはなぜですか?

これらのメソッドは Uint8Array とそのサブクラスにしかありません。Node の Buffer には Node 25 以降で備わります。その他の型付き配列は、メモリを包んでください:new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength)。

続けて読む