上传文件
  1. base64hub
  2. 在代码中使用 Base64
  3. Python
代码

Python 中的 Base64

标准库的 base64 模块什么都能处理:标准和 URL 安全字母表、文件以及 MIME 分行。下面的每段代码都在 Python 3.13 上运行过。

编码和解码字符串

Python 的 Base64 函数处理的是字节,所以文本要先编码为 UTF-8,最后再解码回来。漏掉其中任何一步,都是最常见的错误。

import base64

s = "héllo wörld"
encoded = base64.b64encode(s.encode("utf-8")).decode("ascii")
print(encoded)                                    # aMOpbGxvIHfDtnJsZA==

decoded = base64.b64decode(encoded).decode("utf-8")
print(decoded)                                    # héllo wörld

b64decode 既接受 str 也接受 bytes,但 b64encode 只接受类字节对象。传入字符串会抛出 TypeError: a bytes-like object is required, not 'str'。

想手动检查结果,可以把它粘贴到 Base64 解码器 中,它在浏览器本地运行。

带填充和不带填充的 URL 安全 Base64

URL 安全字母表把 + 换成 -,把 / 换成 _(见 Base64 与 Base64URL 的区别)。Python 会保留 = 填充;如果目标格式(JWT、许多 API)不需要填充,就自己去掉。

data = b"<<???>>"
print(base64.b64encode(data))                     # b'PDw/Pz8+Pg=='
print(base64.urlsafe_b64encode(data))             # b'PDw_Pz8-Pg=='
print(base64.urlsafe_b64encode(data).rstrip(b"="))  # b'PDw_Pz8-Pg'

解码不带填充的输入时,需要把填充补回去,因为 Python 坚持要求长度是 4 的倍数:

def b64url_decode(s: str) -> bytes:
    return base64.urlsafe_b64decode(s + "=" * (-len(s) % 4))

print(b64url_decode("PDw_Pz8-Pg"))                # b'<<???>>'

b64encode 和 b64decode 还接受一个 altchars 参数,即用来替换 +/ 的两字节字符串。altchars=b"-_" 等价于 URL 安全的函数。

print(base64.b64decode("PDw_Pz8-Pg==", altchars="-_"))  # b'<<???>>'

编码和解码文件

对于能放进内存的文件,读取字节后一次调用即可完成编码:

from pathlib import Path

b64 = base64.b64encode(Path("hello.txt").read_bytes()).decode("ascii")
print(b64)                                        # aGVsbG8K
Path("hello-copy.txt").write_bytes(base64.b64decode(b64))

临时转换一次、不想写代码时,文件转 Base64 工具可以在浏览器中完成同样的事。

大文件与流式处理

Base64 把每 3 个输入字节映射为 4 个输出字符,所以只要除最后一块外每块都是 3 字节的倍数,就可以分块编码数据流。这样中间不会出现填充,拼接后的输出与一次调用编码的结果相同。

CHUNK = 3 * 1024 * 256  # 768 KiB, a multiple of 3

with open("big.bin", "rb") as src, open("big.b64", "wb") as dst:
    while chunk := src.read(CHUNK):
        dst.write(base64.b64encode(chunk))

我们在一个 100 KB 的随机文件上与 base64 -w 0 做了对比:逐字节相同。分块解码也是同样的道理,块大小取 4 个字符的倍数即可,前提是输入中没有换行符。

MIME 换行

电子邮件和一些遗留系统要求每行最多 76 个字符(RFC 2045)。encodebytes 会这样做,base64.encode / base64.decode 则在文件对象之间完成同样的处理:

print(base64.encodebytes(b"x" * 60))
# b'eHh4eHh4...eHh4\neHh4\n'   (76 chars, newline, rest, newline)

with open("big.bin", "rb") as src, open("big.mime", "wb") as dst:
    base64.encode(src, dst)
with open("big.mime", "rb") as src, open("big.out", "wb") as dst:
    base64.decode(src, dst)

注意结尾的换行符:encodebytes 总会加一个。在线版本见 MIME Base64 编码器。

校验输入

默认情况下 b64decode 是宽松的:它在解码前会扔掉字母表之外的任何字符。这会掩盖 bug。传入 validate=True 让它变得严格。

import binascii

print(base64.b64decode("aGk=!!"))                 # b'hi'  (junk ignored)

try:
    base64.b64decode("aGk=!!", validate=True)
except binascii.Error as e:
    print(e)                                      # Only base64 data is allowed

使用 validate=True 时,换行符也算无效字符,所以要先去掉 MIME 换行。你会遇到的错误都属于 binascii.Error 类型(ValueError 的子类):

输入错误信息
aGkIncorrect padding
aInvalid base64-encoded string: number of data characters (1) cannot be 1 more than a multiple of 4
aGk==,validate=TrueExcess padding not allowed
aGk=!!,validate=TrueOnly base64 data is allowed

Base64 错误指南 解释了每种错误的成因,Base64 校验工具 会显示字符串在哪里出错。

常见陷阱

把 URL 安全的输入交给 b64decode。不加 validate=True 时,- 和 _ 会被悄悄丢掉,你得到的是乱码而不是报错:

print(base64.b64decode("PDw_Pz8-Pg=="))           # b'<<\x0f\xcf\xc3\xe0'
print(base64.urlsafe_b64decode("PDw_Pz8-Pg=="))   # b'<<???>>'

urlsafe_b64decode 同样接受标准输入,所以当你不确定收到的是哪种字母表时,它是更安全的默认选择。

把二进制数据当文本解码。如果载荷是图片或 ZIP,.decode("utf-8") 会以 UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte 失败。请保持字节形式,并写入文件。

输出中的 b'...'。打印 b64encode 的返回值会显示一个字节字面量。如果这个 b' 跑进了 JSON 或请求头里,说明你忘了 .decode("ascii")。

把 Base64 当作保护手段。它只是一种编码,任何人都能还原。见 编码与加密的区别。

版本说明

  • base64.b64encode、b64decode、urlsafe_* 和 altchars 在所有 Python 3 版本中都存在。
  • 从 Python 3.3 起,b64decode 接受 ASCII str 输入(不仅限于字节)。
  • encodestring 和 decodestring 在 3.9 中被移除;请使用 encodebytes 和 decodebytes。
  • binascii.b2a_base64(data, newline=False)(3.6+)是 b64encode 背后的底层函数,对 b"hi" 返回 b'aGk='。
  • 同一个模块还提供 b32encode、b16encode、b85encode 和 a85encode,用于其他字母表。

常见问题

为什么 b64encode 返回的是 b'...' 而不是字符串?

Python 的 Base64 函数接受字节,也返回字节。对结果调用 .decode("ascii") 才能得到 str;对文本编码之前,要先调用 .encode("utf-8")。

在 Python 中如何解码没有填充的 Base64?

解码前把缺失的 = 补回去:s + "=" * (-len(s) % 4)。对于不带填充的输入,Python 会以 binascii.Error: Incorrect padding 拒绝。

b64decode 和 urlsafe_b64decode 有什么区别?

urlsafe_b64decode 会在解码前把 - 和 _ 转换为 + 和 /。普通的 b64decode 除非传入 validate=True,否则会把 - 和 _ 当作垃圾字符悄悄丢掉,结果得到错误的字节而不是报错。

base64.encodestring 还能用吗?

不能。encodestring 和 decodestring 已在 Python 3.9 中移除。需要带换行的 MIME 风格输出,请用 encodebytes 和 decodebytes。

继续阅读