Base64编码

把每行原文按 RFC 4648 的标准字母表编成 4 字符一组的结果,中文先转 UTF-8 字节,末尾按余数补齐填充,并把 btoa() 拒绝非 Latin-1 字符的原因直接说清楚。

有效行数4 行
原文字节合计19 字节
输出字符合计32 字符
填充合计5 个 =
[1] A(1 字节,补 2 个 =)
    QQ==
[2] AB(2 字节,补 1 个 =)
    QUI=
[3] ABC(3 字节,无填充)
    QUJD
[4] 柳泉工具A(13 字节,补 2 个 =)
    5p+z5rOJ5bel5YW3QQ==

提示:输入框里按 Ctrl + Enter(macOS 用 Cmd + Enter)立刻编码一次;Enter 留给换行,因为这里一行就是一段独立的原文。文本框为空时不会自动填示例,点「载入示例」可以随时还原上面那四行。

使用方法

  1. 把要编码的原文粘进上面的输入框,一行一段独立原文。页面打开时框里已经放好四行示例(A、AB、ABC、柳泉工具A),结果区显示的就是它们各自编码后的结果,不用先点按钮。
  2. 选项按需勾选。默认勾着「按 UTF-8 转字节再编码」,这是中文能出结果的前提;「输出 URL-safe 字母表」把第 62、63 号符号从 +、/ 换成 -、_;「按 MIME 每 76 字符折行」会在每满 76 个字符后插入 CRLF,宽度可以在输入框里改成 4~998 之间的整数。
  3. 示例第 4 行 柳泉工具A 是 4 个汉字加 1 个 ASCII 字母,UTF-8 下 4×3+1 = 13 字节,编出来是 5p+z5rOJ5bel5YW3QQ==,末尾补 2 个 =——因为 13 除以 3 余 1。
  4. 想看清 btoa() 的真实脾气,把「按 UTF-8 转字节再编码」取消勾选再运行一次。工具会当场停下并指出是第几行第几个字符、码点是多少,而不是丢给你一串乱码。
  5. 输入框里按 Ctrl + Enter(macOS 用 Cmd + Enter)立刻编码。全部处理完点「复制结果」把整段带走,或者从下面的对照表导出 CSV。

这个 Base64编码工具能解决什么问题

一是把中文塞进只认 ASCII 的通道。邮件头、HTTP 基本认证、data URI、以及不少只允许 7 位字符的遗留协议,都不接受非 ASCII 字节。Base64编码把任意字节映射到 A–Z a–z 0–9 + / 这 64 个符号上,代价是体积涨三分之一,好处是这串字符在传输链路的每一段都不会被改写。

二是把二进制内容写成一行文本。证书、密钥、压缩包的片段要贴进配置文件、写进 JSON 字段、放进 issue 正文时,直接贴二进制一定会被编辑器或终端破坏,编码成 Base64 之后就可以当普通字符串处理。

三是弄明白「为什么我编出来的中文是乱码」。绝大多数在线工具只调用 btoa(str),而它按 HTML 标准只接受码点 0~255 的字符。中文汉字码点远大于 255,现代浏览器会直接抛 InvalidCharacterError,老工具则用 unescape(encodeURIComponent(s)) 硬绕。本页把这一步显式拆成「TextEncoder 转 UTF-8 字节 → 逐字节转字符 → btoa」,两边结果都能对着看。

四是避开 URL 传参的经典坑。标准字母表里的 + 在查询串中会被服务端按 application/x-www-form-urlencoded 规则解析成空格,/ 又会撞上路径分隔符。勾上「输出 URL-safe 字母表」把这两个符号换成 - 与 _,这串结果就能直接放进路径或参数里。

本页不做这些事:不把已经编码的串还原成原文、不给令牌拆分头部与载荷、不把文件整体读成 Base64。要反向操作请用页面底部的「Base32编码」查看同族的另一种字母表,或者去「Base64解码」把结果还原回文字。

编码原理:RFC 4648 §4 字母表、3 字节拆 4 个 6 位与填充规则

这个 Base64编码工具的核心只有三步:new TextEncoder().encode(str) 把原文变成 UTF-8 字节数组,再把字节按 3 个一组拼成 24 位、切成 4 个 6 位索引去查字母表,最后按剩余字节数补 =。三步里任何一步省略,结果都会和标准实现不一样。

字母表是 RFC 4648 第 4 节定义的 64 个符号,顺序固定不可换。下标 0~25 是大写 A–Z,26~51 是小写 a–z,52~61 是数字 0–9,62 是 +,63 是 /。以 ABC 为例:A 是 0x41、B 是 0x42、C 是 0x43,拼成 24 位是 01000001 01000010 01000011;按 6 位重切得到 010000、010100、001001、000011,也就是十进制 16、20、9、3,查表正好是 Q、U、J、D,连起来就是 QUJD。这就是「每 3 个字节换 4 个字符」的全部内容。

填充的个数由末尾剩下的字节数唯一决定。余 0 字节(正好整除)时不补,例如 ABC → QUJD;余 1 字节时补 2 个 =,例如 A → QQ==;余 2 字节时补 1 个 =,例如 AB → QUI=。补齐的位一律填 0,解码端靠填充数量反推「最后一组只该取几个字节」,所以 = 不是装饰,去掉它解码就会多出垃圾字节——这也是同一段文本在不同系统里长度不一致的常见原因。另外整除时不会也不该出现 ==== 这种四连填充。

中文必须走 UTF-8 字节,绕不开 btoa() 的限制。按 HTML 标准,btoa() 接受的字符串要求每个码点都落在 0~255,也就是 Latin-1 范围;柳 的码点是 U+67F3,换算成十进制是 26611,远超 255,直接传进去在 Chrome、Firefox、Safari 上都会抛 InvalidCharacterError: The string to be encoded contains characters outside of the Latin1 range. 正确顺序是先用 TextEncoder 把它编成 E6 9F B3 三个字节,再把这三个字节逐个 String.fromCharCode 拼成一个「每个字符码点都不超过 255」的临时串交给 btoa()。跳过这一步而直接用 Latin-1 处理 U+67F3,只会得到 0xF3 一个字节,结果自然对不上。

输出长度是 4 × ceil(n / 3),与输入内容无关。1024 字节固定得到 1368 个字符,100 字节得到 136 个字符,1 MiB(1048576 字节)分成 349526 组、输出 1398104 个字符,膨胀比恒为 4/3,也就是 +33.33%。注意这是逐段编码的下界:本页按行独立编码,每行各自补齐,所以四行示例 19 字节输出 32 个字符,而把这 19 字节当作一整块编码只需要 28 个字符——短行越多,填充带来的额外开销越大。

URL-safe 是另一个字母表,不是同一套的变体。RFC 4648 第 5 节把 62、63 号符号换成 - 和 _,其余 62 个符号完全一致,也没有版本号或自动识别机制。含 + 的串丢进标准解码器会被当成非法字符,反过来含 - 的串丢进标准解码器也一样。MIME 折行则来自 RFC 2045:base64 正文单行不得超过 998 个字符,推荐每 76 个字符后插入 CRLF,本页默认宽度就取 76。

完整示例(数值仅作演示)

示例条件与页面打开时的默认状态完全一致:输入框四行原文、勾选「按 UTF-8 转字节再编码」、不勾 URL-safe、不折行。四行实测结果如下:

四行原文字节合计 1+2+3+13 = 19 字节,输出字符合计 4+4+4+20 = 32 字符,填充合计 2+1+0+2 = 5 个。逐行核算:4×ceil(1/3)=4、4×ceil(2/3)=4、4×ceil(3/3)=4、4×ceil(13/3)=20,四个数加起来正好 32。第 4 行再把字节拆开看:E6 9F B3 E6 B3 89 E5 B7 A5 E5 85 B7 41,前三字节 E6 9F B3 就是汉字「柳」的 UTF-8 编码,最后一字节 41 是大写字母 A。

把这 19 字节当作一整块连续数据编码,只需要 4×ceil(19/3) = 28 个字符;按行独立编码却输出了 32 个字符,多出来的 4 个字符就是四行末尾各自补齐 = 的开销。如果你在意长度,把多行合并成一行再编码能省下这部分。

输入原文与编码结果对照表

下表覆盖本工具最常见的几类输入。注意「输出字符数」一列只跟字节数有关,跟原文看起来长短无关;汉字一个字 3 字节、ASCII 一个字符 1 字节、emoji 一个 4 字节,混在一起凭肉眼估一定估错。

输入原文UTF-8 字节数输出字符数末尾填充Base64 结果
A142 个 =QQ==
AB241 个 =QUI=
ABC34无QUJD
ABCD482 个 =QUJDRA==
12345581 个 =MTIzNDU=
12345668无MTIzNDU2
柳34无5p+z
柳泉68无5p+z5rOJ
柳泉工具1216无5p+z5rOJ5bel5YW3
柳泉工具A13202 个 =5p+z5rOJ5bel5YW3QQ==
🎉(单个 emoji)482 个 =8J+OiQ==
Hello581 个 =SGVsbG8=
柳泉工具B13202 个 =5p+z5rOJ5bel5YW3Qg==
一个半角空格142 个 =IA==
1142 个 =MQ==

输入范围、边界与精度说明

常见问题

为什么中文丢进去会报「字符超出 Latin-1 范围」?

因为 btoa() 按 HTML 标准只接受每个码点都不超过 255 的字符串。汉字 柳 的码点是 U+67F3(十进制 26611),远远超标,浏览器只能抛错。勾上「按 UTF-8 转字节再编码」,工具会先用 TextEncoder 把它编成 E6 9F B3 三个字节再交给 btoa(),问题就消失了。

为什么编码后比原文长了三分之一?

因为每个 6 位都要占一个完整字符。3 字节 = 24 位,拆成 4 个 6 位正好 4 个字符,所以长度是 4 × ceil(n / 3),比值恒为 4/3。这是 Base64 的本质代价,不是实现缺陷——它换来的是「输出只含 64 个可打印 ASCII 符号」这个性质。

末尾的 = 可以手动删掉吗?

技术上很多解码器能自动补回来,但标准里 = 是编码结果的一部分,删掉之后这串字符在严格校验的场景(例如签名比对、长度断言)就会对不上。要求不带填充的场合(比如某些 JWT 实现)应当整体改用 URL-safe 字母表并按对方的规则处理,而不是随手删符号。

同一段文字,为什么我和同事编出来的结果不一样?

三种可能:一是勾选的选项不同(URL-safe、折行都会改变输出);二是原文末尾多带了空格或换行;三是他那边先做了别的处理再编码。换行最容易出错——Windows 记事本默认 CRLF,编辑器和网页表单多给 LF,两者差一个 0x0A 字节,编出来自然不同。

把编码结果放进网址参数后,服务端收到的内容变了?

大概率是 + 被当成空格了。查询串默认按 application/x-www-form-urlencoded 解析,其中的 + 会被还原成空格,标准字母表里的 + 正好中招。改成勾选「输出 URL-safe 字母表」把 + 换成 -,或者让调用方对整串再做一次百分号编码。

折行用 LF 还是 CRLF?

本页按 RFC 2045 的约定输出 CRLF(0x0D 0x0A),这是 MIME 正文的标准行结束符。如果目标系统明确要求 LF,可以折行导出之后自行替换,或者干脆不折行、交给对方处理。折行只是排版,不影响解码结果——按 RFC 4648 第 3 节,解码时本来就应该忽略换行与空白。

编码结果能当密文用吗?

不能。Base64 是编码不是加密,没有密钥、没有混淆,任何人拿到字符串都能反着算回原文。它唯一的「保护」是让内容看起来不像明文,这在安全上没有任何意义。要保密请用真正的加密算法,并把密钥和密文分开存放。

输入的内容会不会被上传?

不会。字节转换、字母表查表、填充补齐、折行全部在页面里完成,没有任何网络请求,也不写浏览器存储。想自己确认,打开开发者工具的 Network 面板连点几次「开始编码」,可以看到没有新请求发出。

相关工具