Uint8Array to/from Base64 S4
中文标题:Uint8Array 与 Base64 之间的转换
- 阶段: Stage 4
- 状态: 已完成
- ECMAScript 版本: ES2026
- 同步时间: 2026年8月26日
- English original · 官方仓库
该提案添加了 Uint8Array.prototype.toBase64() 和 toHex(),以及静态方法 fromBase64() 和 fromHex(),用于在二进制数据和 base64 或十六进制字符串之间进行转换。它还引入了 setFromBase64/setFromHex 用于写入现有数组,并提供选项来处理字母表、最后块处理和填充。
以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。
Uint8Array 与 base64 和 hex 之间的转换
base64 是一种将任意二进制数据表示为 ASCII 的常见方式。JavaScript 有 Uint8Array 来处理二进制数据,但没有内置机制将这些数据编码为 base64,也没有机制将 base64 数据转换为对应的 Uint8Array。本提案旨在解决这个问题。它还添加了在十六进制字符串和 Uint8Array 之间进行转换的方法。
它目前处于 TC39 流程 的第 4 阶段:它已完整地包含在标准中。任何进一步的更改都需要通过 常规流程 在单独的提案中进行。此仓库已存档。
在 playground 上试用。
规范文本可在 此处 获得,test262 测试在 此 PR 中。
实现者可能对 开源 simdutf 库 感兴趣,该库提供了 base64 解码器的快速实现,当未指定任何选项调用 Uint8Array.fromBase64(string) 时,其行为与之匹配(包括处理空白)。截至撰写本文时,它仅适用于 latin1 字符串,但 utf16 版本 可能即将推出。
基本 API
这将添加 Uint8Array.prototype.toBase64/Uint8Array.prototype.toHex 和 Uint8Array.fromBase64/Uint8Array.fromHex 方法。如果传入的字符串没有正确编码,后者会抛出异常。
Base64 选项
在选项包参数中提供了额外的选项:
-
alphabet:允许将字母表指定为base64或base64url。 -
lastChunkHandling:回想一下,base64 解码一次处理 4 个字符的块,但输入可能有一些字符不能均匀地分成 4 个字符的块。此选项决定应如何处理最后的字符块。三个选项是"loose"(默认),它将块视为具有任何必要的=填充(但如果不可能,则抛出异常,即恰好有一个多余字符);"strict",它强制块恰好有 4 个字符(计算=填充),并且 溢出位 为 0;以及"stop-before-partial",除非最后的块恰好有 4 个字符,否则在最后的块之前停止解码。 -
omitPadding:编码时,是否包含=填充。默认为false,即包含填充。
hex 方法不接受任何选项。
写入现有的 Uint8Array
Uint8Array.prototype.setFromBase64 方法允许写入现有的 Uint8Array。类似于 TextEncoder encodeInto 方法,它返回一个 { read, written } 对。
此方法接受一个可选的最终选项包,选项与上述相同。
与 encodeInto 一样,不显式支持写入目标数组的指定偏移量,但您可以通过创建子数组来实现。
Uint8Array.prototype.setFromHex 除了十六进制外完全相同。
流式处理
没有显式的流式处理支持。但是,在此 API 之上,在用户空间中相对容易高效地实现,并支持与底层函数相同的所有选项。
常见问题解答
标准、其他语言和现有 JavaScript 库中的 base64 实现之间存在哪些差异?
我有一 整页关于此的页面,包含表格、脚注等。变异的空间相对较小,但语言和库设法探索了几乎所有的空间。
总而言之,base64 编码器可能在以下方面有所不同:
- 标准或 URL 安全字母表
- 输出中是否包含
= - 是否在特定数量的字符后添加换行符
解码器可能在以下方面有所不同:
- 标准或 URL 安全字母表
- 输入中是否需要
=,以及如何处理格式错误的填充(例如额外的=) - 是否对非零填充位失败
- 行长度是否必须有限
- 如何处理非 base64 字母表的字符(有时仅对子集进行特殊处理,例如空白)
支持哪些字母表?
对于 base64,您可以指定 base64 或 base64url 用于编码器和解码器。
对于 hex,小写和大写字符(包括在同一字符串中混合)都可以成功解码。输出始终为小写。
如何处理额外的填充位?
如果输入数据的长度不是 3 字节的倍数,则编码时,最后的 1 或 2 个字节将使用 2 或 3 个 base64 字符进行编码。由于每个 base64 字符是 6 位,这意味着您将使用 12 或 18 位来表示 8 或 16 位,这意味着您有额外的 4 或 2 位不编码任何内容。
根据 RFC,解码器可以拒绝填充位非零的输入字符串。此处,除非指定了 lastChunkHandling: "strict",否则非零填充位会被静默忽略。
如何处理空白?
编码器不输出空白。hex 解码器不允许空白作为输入。base64 解码器允许字符串中的任何位置出现 ASCII 空白。
如何处理其他字符?
任何其他字符的存在都会导致异常。
为什么这些是同步的?
实际上,我遇到的大多数 base64 编码的数据大约有几百字节(例如 SSH 密钥),可以极其快速地编码和解码。我认为,要求使用 Promise 来处理此类数据将是一种遗憾,尤其是考虑到目前人们使用的替代方案似乎都是同步的。
为什么只支持这些编码?
虽然还存在其他字符串编码,但没有一个像这两种一样常用。
为什么不直接使用 atob 和 btoa?
这些方法接受和消耗字符串,而不是在字符串和 Uint8Array 之间进行转换。
为什么不使用 TextEncoder?
base64 不是文本编码格式;不涉及 码点。因此,尽管符合 TextEncoder/TextDecoder 的类型签名,但 base64 编码和解码在概念上不适合那些 API 去做。
这也是之前 讨论 中达成的共识。
如果我只想编码 ArrayBuffer 的一部分怎么办?
Uint8Array 可以是底层缓冲区的部分视图,因此您可以创建这样的视图并在其上调用 .toBase64。