Base64 在线编码解码:它不是加密,中文乱码的根因也在这

把明文丢进 Base64 在线编码解码工具,得到一串看不懂的字符,很多人就以为加密了。Base64 不加密 —— 它只是把二进制数据换成 64 个可打印字符,任何人贴回去解码都能还原原文。

搞懂它的三个特性(3 字节变 4 字符、= 补位、字符集决定乱码),基本能解决 90% 的线上问题。

一、Base64 不是加密,只是编码

对比项 编码(Base64) 加密(AES 等)
目的 让二进制能走只支持文本的通道 让没有密钥的人读不出来
需要密钥吗 不需要 需要
逆向难度 任何人都可以直接解 没密钥解不出来
典型用途 邮件附件、Data URI、HTTP Basic 认证、JWT 存储密码、传输敏感数据

HTTP Basic 认证里的 Authorization: Basic dXNlcjpwYXNz 就是 user:pass 的 Base64,等同于明文,必须配合 HTTPS。同理 JWT 的 payload 也只是 Base64,别往里放密码。

二、3 字节变 4 字符:体积为什么涨三分之一

原理只有一句:取 3 个字节共 24 bit,切成 4 组每组 6 bit,6 bit 取值 0~63,再查表映射成字符。字符表是 A-Z(26)+ a-z(26)+ 0-9(10)+ + /(2)= 64 个。

所以输入输出的比例固定是 3 : 4,编码后长度 = 4 × ceil(字节数 / 3),必然是 4 的倍数,体积约 +33%

三、宽字符换算表:为什么中文编出来特别长

Base64 按字节算,不按字符算。UTF-8 下一个汉字占 3 字节,一个 emoji 占 4 字节,这才是长度暴涨的原因:

输入 UTF-8 字节数 Base64 字符数 末尾 =
a 1 4 2
ab 2 4 1
abc 3 4 0
abcd 4 8 2
3 4 0
中文 6 8 0
997 工具箱 13 20 2
𠮷 4 8 2

四、末尾的 = 是什么

最后一组凑不满 3 字节时,用 = 占位:缺 1 个字节补 1 个 =,缺 2 个字节补 2 个 =,正好 3 字节时没有 =

所以反过来说:一段 Base64 去掉 = 之后,接收方只要看剩余长度 mod 4 是 2 还是 3,就知道该补几个等号(余 1 是不可能出现的情况,出现就说明传丢了字符)。

= 只是长度标记,不携带信息,所以很多场景(URL-safe、JWT)会把它去掉,解码时再按长度补回来。判断一段 Base64 是否完整,第一件事就是看长度是不是 4 的倍数。

五、标准表 +/ 与 URL-safe -_

标准字符表的第 62、63 位是 +/,这两个字符在 URL 和文件名里有特殊含义 —— 尤其是 +,在 query string 里会被解成空格。RFC 4648 因此定义了 URL-safe 变体:

位置 标准 Base64 URL-safe Base64
62 + -
63 / _
补位 保留 = 通常去掉
典型场景 邮件、Data URI、Basic 认证 URL / 文件名 / JWT / Cookie
// 两者互转,就两行
const toUrlSafe = (s) => s.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
const toStandard = (s) => s.replace(/-/g, '+').replace(/_/g, '/')

在线工具一般把这个做成开关:勾上之后编码输出会自动换成 -_ 并去掉尾部 =,解码时再自动补回等号。

还有两个常被忽略的细节:一是别把两种变体混用,一段 URL-safe 的串丢给只认标准表的解码器,-_ 会直接判为非法字符;二是 URL-safe 只是换了字符表,没有改变体积比例,别指望去掉 = 能省多少流量。

六、中文乱码的真正根因

坑一:btoa 遇到中文直接抛错。

btoa('中文')
// ❌ Uncaught InvalidCharacterError: String contains an invalid character

原因:btoa 把每个字符按 Latin-1(单字节,0~255) 处理,中文的码位远超 255,直接报错。正确做法是先转成 UTF-8 字节再编码:

const enc = (str) => btoa(String.fromCharCode(...new TextEncoder().encode(str)))
const dec = (b64) => new TextDecoder().decode(Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)))

enc('997 工具箱')   // 'OTk3IOWbvuW6lOi0pQ=='
dec('OTk3IOWbvuW6lOi0pQ==')  // '997 工具箱'

注意 String.fromCharCode(...bytes) 在几百 KB 以上会爆调用栈,大文件要按块拼接(常见做法是每 0x8000 字节一段)。

坑二:编码端和解码端字符集不一致。 老系统按 GBK 生成的字节流被当成 UTF-8 解,出来就是经典的 锟斤拷。统一 UTF-8 是唯一解;必须收 GBK 时用 iconv -f gbk -t utf-8 转。

在线工具如果只写 btoa/atob,中文一定是报错或乱码,选工具时认准写「UTF-8 安全」的:

👉 Base64 在线编码解码(UTF-8 安全,支持中文和图片预览)

七、Data URI 图片:方便但有代价

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...

好处是零请求、不怕图床挂。代价有三条:

  1. 体积 +33%,同样一张图要多传三分之一流量
  2. 无法单独缓存,每次都必须随 HTML / CSS 一起下载,改一个字整段失效
  3. 阻塞首屏,内联在 CSS 里的 base64 会拖慢样式解析

经验值:小于 2~4KB 的小图标内联划算;超过 10KB 就老实用 URL。要验证一段 base64 是不是你想要的图,解码后直接预览最快。

生成和验证在命令行里也就两行:

# 图片转 base64(Linux 记得加 -w 0,否则每 76 字符插一个换行)
printf 'data:image/png;base64,%s' "$(base64 -w 0 logo.png)" > logo.txt

# 反过来:剥掉 data URI 前缀再解码成文件
sed 's/^data:image\/png;base64,//' logo.txt | base64 -d > logo.png

顺带一个实用技巧:不用解码也能看出文件类型,看开头几个字符即可,因为它们由文件头字节决定:

Base64 开头 文件类型
iVBORw0KGgo PNG
/9j/ JPEG
R0lGOD GIF
JVBERi0 PDF
UEsDB ZIP / Office 文档

八、JWT 三段里为什么是 Base64URL

JWT 形如 header.payload.signature,前两段是 Base64URL 编码的 JSON,第三段是对前两段做 HMAC 后得到的二进制再 Base64URL。用 URL-safe 而不是标准 Base64,是因为 JWT 要放进 URL、Cookie 和 Authorization 头,+ / = 都会引发转义问题。

反过来也说明:JWT 的 payload 一贴进解码工具就能看到明文,它是签名不是加密 —— 签名保证没被篡改,不保证不被看见。

九、命令行怎么用

# 编码(务必用 -n 或 printf,否则换行会被编进去)
echo -n '997 工具箱' | base64
printf '%s' '中文' | base64

# Linux(GNU coreutils):-w 0 表示不插换行
base64 -w 0 file.png > file.b64
# macOS(BSD):用 -i / -o
base64 -i file.png -o file.b64

# 解码
echo 'OTk3IOWbvuW6lOi0pQ==' | base64 -d
base64 -d file.b64 > file.png
base64 -D file.b64 > out.png      # 老版本 macOS 用 -D 不是 -d

# openssl(两边通用)
openssl base64 -in file.png -out file.b64
openssl base64 -d -in file.b64 -out file.png

最常见的低级错误是 echo 'x' | base64 没加 -n:换行符被一起编码,解码出来末尾多一个空行,和预期结果怎么比对都对不上。

第二个坑是 GNU 和 BSD 参数不同:Linux 上是 base64 -d -w 0,macOS 自带的 BSD 版没有 -w,解码要用 -D(新版本也支持 -d)。脚本里要跨机器跑,直接用 openssl base64 最稳。

十、常见报错对照表

报错 / 现象 原因 解决
InvalidCharacterError(编码时) btoa 遇到中文 / emoji 等非 Latin-1 字符 TextEncoder 转 UTF-8 字节
InvalidCharacterError(解码时) 含空格、换行,或长度不是 4 的倍数 先 trim、去掉换行,URL-safe 补回 =
解码出来是 锟斤拷 GBK 字节流被当 UTF-8 解 iconv -f gbk -t utf-8
解码结果末尾多一个空行 编码时用了不带 -necho 改用 echo -n / printf '%s'
两个系统编出来的串对不上 一边用了 URL-safe,一边没有 统一 -_+/
URL 传参后解码失败 + 在 query 里被当成空格 换 URL-safe,或编码后再 encodeURIComponent
解出来的 JSON 报 Unexpected token 原文不是 JSON,或带 BOM 参考 JSON 在线格式化的排错清单

十一、速查表

编码后长度 = 4 × ceil(字节数 / 3)     # 一定是 4 的倍数
体积开销   = 33%
补位规则   = 缺 1 字节补 1 个 =,缺 2 字节补 2 个 =
标准表     = A-Z a-z 0-9 + /         # 第 62、63 位是 + /
URL-safe   = A-Z a-z 0-9 - _         # 通常去掉 =
UTF-8      = 汉字 3 字节,emoji 4 字节

记不住细节没关系,记住「它是编码不是加密」和「中文要先转 UTF-8」这两条,就不会出安全事故、也不会出乱码。


相关工具:Base64 编码解码JSON 格式化JSON 转 CSV/Excel

全部纯前端实现,输入的内容不上传服务器。

还有 53 个免费在线工具

纯前端实现,不用注册,数据不上传服务器。

浏览全部工具