Uint8Arrayとbase64、Hex(16進数)の相互変換
Uint8Arrayとbase64、Hex(16進数)の相互変換を行うメソッドがBaseline 2025で追加されました。fromBase64やtoHexなどメソッドが標準で扱えるようになり、複雑な変換処理を自作せず安全かつ簡潔に実装できます。
はじめに
base64やHexは、バイナリデータをテキスト形式で表現するための方法です。
そして、JavaScriptではバイナリデータを扱うためにUint8Arrayを用います。
これまでは、これらの表現を互いに変換するメソッドが用意されていませんでした。 しかし、Chrome 140のリリースによりこれらの変換メソッドがBaselineの2025で利用可能になりました。
変遷
これまでは、変換処理を自前で組み込む必要がありました。
以下に示すのは簡易的な実装であり、すべてのケースで正確に動作するとは限りません(今回追加されるメソッド相当の実装は polyfillが参考になります)。
// Uint8Array → base64
function uint8ArrayToBase64(uint8Array: Uint8Array): string {
return btoa(String.fromCharCode(...uint8Array));
}
console.log(uint8ArrayToBase64(new Uint8Array([72, 101, 108, 108, 111]))); // "SGVsbG8="
// base64 → Uint8Array
function base64ToUint8Array(base64: string): Uint8Array {
return new Uint8Array([...atob(base64)].map(c => c.charCodeAt(0)));
}
console.log(base64ToUint8Array("SGVsbG8=")); // Uint8Array([72, 101, 108, 108, 111])
// Uint8Array → Hex
function uint8ArrayToHex(uint8Array: Uint8Array): string {
return [...uint8Array].map(b => b.toString(16).padStart(2, '0')).join('');
}
console.log(uint8ArrayToHex(new Uint8Array([72, 101, 108, 108, 111]))); // "48656c6c6f"
// Hex → Uint8Array
function hexToUint8Array(hex: string): Uint8Array {
const bytes = [];
for (let i = 0; i < hex.length; i += 2) {
bytes.push(parseInt(hex.substr(i, 2), 16));
}
return new Uint8Array(bytes);
}
console.log(hexToUint8Array("48656c6c6f")); // Uint8Array([72, 101, 108, 108, 111])
正確で堅牢な変換処理を自作する必要があり、変換処理を用意するのが大変です。
今後はUint8Arrayのメソッドで簡単に変換できるようになります。
// Uint8Array → base64
const uint8Array = new Uint8Array([72, 101, 108, 108, 111]);
const base64 = uint8Array.toBase64(); // "SGVsbG8="
// base64 → Uint8Array
const base64 = "SGVsbG8=";
const uint8Array = Uint8Array.fromBase64(base64); // Uint8Array([72, 101, 108, 108, 111])
// Uint8Array → Hex
const uint8Array = new Uint8Array([72, 101, 108, 108, 111]);
const hex = uint8Array.toHex(); // "48656c6c6f"
// Hex → Uint8Array
const hex = "48656c6c6f";
const uint8Array = Uint8Array.fromHex(hex); // Uint8Array([72, 101, 108, 108, 111])
Uint8Arrayを生成するときは、静的メソッドであるfromBase64やfromHexで変換し、Uint8Arrayから変換するときはインスタンスメソッドであるtoBase64やtoHexを使用するだけで変換できるようになりました。
追加された機能
先ほど紹介したfromBase64、fromHex、toBase64、toHexに加えて、既存のUint8Arrayに対してデータを設定するためのsetFromBase64とsetFromHexも追加されました。
それぞれの機能について以下で詳しく説明します。
fromBase64
fromBase64は、base64エンコードされた文字列を受け取り、それをデコードして新しいUint8Arrayを生成します。
const base64 = "SGVsbG8=";
const uint8Array = Uint8Array.fromBase64(base64); // Uint8Array([72, 101, 108, 108, 111])
第1引数にはbase64エンコードされた文字列を指定します。第2引数にはオプションで、alphabetとlastChunkHandlingを指定できます。
const base64 = "SGVsbG8=";
const uint8Array = Uint8Array.fromBase64(base64, {
alphabet: "base64url",
lastChunkHandling: "strict"
});
alphabetにはbase64のアルファベットを指定します。デフォルト"base64"で標準のbase64アルファベットを受け入れますが、"base64url"を指定するとURLセーフなbase64アルファベットを受け入れるようになります。
const base64url = "SGV-sbG8";
// Uncaught SyntaxError: Found a character that cannot be part of a valid base64 string.
const uint8Array1 = Uint8Array.fromBase64(base64url);
// URLセーフなエンコードされたbase64も受け入れる
const uint8Array2 = Uint8Array.fromBase64(base64url, {
alphabet: "base64url",
});
lastChunkHandlingはbase64文字列の最後のチャンクの処理方法を指定します。
デフォルトは"loose"で最も柔軟です。最後のチャンクが2、3文字の不完全な状態でも可能な限りデコードします。
const loose1 = Uint8Array.fromBase64("SGVsbG8=", { lastChunkHandling: "loose" });
const loose2 = Uint8Array.fromBase64("SGVsbG8", { lastChunkHandling: "loose" });
console.log(loose1); // Uint8Array([72, 101, 108, 108, 111])
console.log(loose2); // Uint8Array([72, 101, 108, 108, 111])
"strict"は最も厳格です。最後のチャンクが=文字でパディングされた4文字の長さであり、オーバーフロービットが0でなければエラーをスローします。
const strict1 = Uint8Array.fromBase64("SGVsbG8=", { lastChunkHandling: "strict" });
const strict2 = Uint8Array.fromBase64("SGVsbG8", { lastChunkHandling: "strict" });
const strict3 = Uint8Array.fromBase64("SGVsbG1=", { lastChunkHandling: "strict" });
console.log(strict1); // Uint8Array([72, 101, 108, 108, 111])
console.log(strict2); // Uncaught SyntaxError: The base64 input terminates with a single character, excluding padding (=).
console.log(strict3); // Uncaught SyntaxError: The base64 input terminates with non-zero padding bits.
"stop-before-partial"を指定すると、不完全なチャンクがあればその前でデコードを停止します。
const loose = Uint8Array.fromBase64("SGVsbG1", { lastChunkHandling: "loose" });
console.log(loose); // Uint8Array([72, 101, 108, 108, 109])
const stopBeforePartial = Uint8Array.fromBase64("SGVsbG1", { lastChunkHandling: "stop-before-partial" });
console.log(stopBeforePartial); // Uint8Array([72, 101, 108])
fromBase64はbase64エンコードされた文字列をデコードするatob関数よりも利用が推奨されています。
fromHex
fromHexは、Hexエンコードされた文字列を受け取り、それをデコードして新しいUint8Arrayを生成します。
const hex = "48656c6c6f";
const uint8Array = Uint8Array.fromHex(hex); // Uint8Array([72, 101, 108, 108, 111])
fromHexにはオプションはありません。
toBase64
toBase64は、Uint8Arrayのインスタンスメソッドであり、その内容をbase64エンコードされた文字列に変換します。
const uint8Array = new Uint8Array([72, 101, 108, 108, 111]);
const base64 = uint8Array.toBase64(); // "SGVsbG8="
引数にはオプションとしてalphabetとomitPaddingを付与できます。
alphabetはfromBase64と同様に、base64のアルファベットを指定します。デフォルトは"base64"です。
const uint8Array = new Uint8Array([72, 101, 126, 177, 177, 188]);
const base64 = uint8Array.toBase64(); // "SGV+sbG8"
const base64Url = uint8Array.toBase64({ alphabet: "base64url" }); // "SGV-sbG8"
omitPaddingは、エンコードされたbase64文字列の末尾の=パディングを省略するかどうかを指定します。デフォルトはfalseでパディングを含みます。
const uint8Array = new Uint8Array([72, 101, 108, 108, 111]);
const base64 = uint8Array.toBase64(); // "SGVsbG8="
const base64WithoutPadding = uint8Array.toBase64({ omitPadding: true }); // "SGVsbG8"
toHex
toHexは、Uint8Arrayのインスタンスメソッドであり、その内容をHexエンコードされた文字列に変換します。
const uint8Array = new Uint8Array([72, 101, 108, 108, 111]);
const hex = uint8Array.toHex(); // "48656c6c6f"
fromHexと同様に、オプションはありません。
setFromBase64
setFromBase64は、Uint8Arrayのインスタンスメソッドであり、base64エンコードされた文字列をデコードして、その内容を既存のUint8Arrayに設定します。
const uint8Array = new Uint8Array(5);
const base64 = "SGVsbG8=";
uint8Array.setFromBase64(base64); // uint8Array is now Uint8Array([72, 101, 108, 108, 111])
オプションはfromBase64と同様に、alphabetとlastChunkHandlingを指定できます。
const uint8Array = new Uint8Array(5);
const base64 = "SGVsbG8=";
// {read: 8, written: 5}
uint8Array.setFromBase64(base64, {
alphabet: "base64url",
lastChunkHandling: "strict"
});
const base64 = "SGVsbG1";
// {read: 4, written: 3}
uint8Array.setFromBase64(base64, {
lastChunkHandling: "stop-before-partial"
});
setFromBase64は返り値にreadとwriteをキーに持つオブジェクトを返します。readは読み取ったbase64文字列の長さ、writeは書き込んだバイト数を示します。
setFromHex
setFromHexは、Uint8Arrayのインスタンスメソッドであり、Hexエンコードされた文字列をデコードして、その内容を既存のUint8Arrayに設定します。
const uint8Array = new Uint8Array(5);
const hex = "48656c6c6f";
// { read: 10, written: 5 }
uint8Array.setFromHex(hex); // uint8Array is now Uint8Array([72, 101, 108, 108, 111])
オプションはもたず、返り値はsetFromBase64と同様にreadとwriteをキーに持つオブジェクトを返します。
おわりに
Chrome 140以降で追加されたこれらの新しいメソッドにより、JavaScriptでのバイナリデータとテキスト形式の変換が大幅に簡素化されました。 従来は複雑な変換処理を自作する必要がありましたが、今後は標準のメソッドを使って安全かつ簡潔に処理できるようになりました。