是什么

  • Base64将每3个二进制字节编码为4个ASCII字符。
  • Base64使用以下字符进行编码:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
  • 如果输入字节数不为3的整数倍,Base64在输出字符串末尾使用字符=补全(padding),使输出字符串长度为4的整数倍。
  • Base64属于定长编码。只要输入字节数不变,Base64编码输出的字符数就不变。Base62、Base58不属于定长编码。

变种

Base64 URL Safe

Base64输出包含+/两个特殊字符,在URL中可能引起问题。
Base64 URL Safe将+/替换为-_,以避免上述问题。

Base62

在Base64基础上,去掉+/两个特殊字符,仅保留数字、字母大小写。

Base58

在Base62基础上,去掉0 (数字0)、I (大写字母 i)、O (大写字母 o) 和 l (小写字母 L),方便人阅读,避免歧义。

实现

Python

编码Base64:

>>> import base64
>>> input_string = 'Base64输出包含+和/两个特殊字符'
>>> input_string_bytes = input_string.encode('utf-8')
>>> print(input_string_bytes)
b'Base64\xe8\xbe\x93\xe5\x87\xba\xe5\x8c\x85\xe5\x90\xab+\xe5\x92\x8c/\xe4\xb8\xa4\xe4\xb8\xaa\xe7\x89\xb9\xe6\xae\x8a\xe5\xad\x97\xe7\xac\xa6'
>>> base64.standard_b64encode(input_string_bytes)  # 标准Base64编码
b'QmFzZTY06L6T5Ye65YyF5ZCrK+WSjC/kuKTkuKrnibnmrorlrZfnrKY='
>>> base64.urlsafe_b64encode(input_string_bytes)  # Base64 URL Safe编码
b'QmFzZTY06L6T5Ye65YyF5ZCrK-WSjC_kuKTkuKrnibnmrorlrZfnrKY='

需要区分两种“编码”:

  • UTF-8将Unicode字符串“编码”为二进制数据;
  • Base64将二进制数据“编码”为ASCII字符串。

传统字符编码(例如UTF-8)将各种字符(特别是汉字等非ASCII字符)转换为二进制数据,以便于计算机本地存储;Base64属于二进制到文本编码,这类编码的目标是在Email、HTML等纯文本格式里表示二进制数据,因此将二进制数据转换为ASCII字符串。

既然Base64将二进制字节编码为ASCII字符,为什么b64encode返回bytes对象?原因主要有两点:一是避免重复来回转换,二是遵守“‘编码’是将字符串转换为二进制”的范式。

解码Base64:

>>> import base64
>>> input_string = 'QmFzZTY06L6T5Ye65YyF5ZCrK+WSjC/kuKTkuKrnibnmrorlrZfnrKY='
>>> base64.standard_b64decode(input_string)
b'Base64\xe8\xbe\x93\xe5\x87\xba\xe5\x8c\x85\xe5\x90\xab+\xe5\x92\x8c/\xe4\xb8\xa4\xe4\xb8\xaa\xe7\x89\xb9\xe6\xae\x8a\xe5\xad\x97\xe7\xac\xa6'
>>> base64.standard_b64decode(input_string).decode('utf-8')
'Base64输出包含+和/两个特殊字符'

Java

编码Base64:

@Test
public void base64EncodeTest() {
    String inputString = "Base64输出包含+和/两个特殊字符";
    byte[] inputStringBytes = inputString.getBytes(StandardCharsets.UTF_8);
    System.out.println(new String(Base64.getEncoder().encode(inputStringBytes), StandardCharsets.US_ASCII));
    // 打印"QmFzZTY06L6T5Ye65YyF5ZCrK+WSjC/kuKTkuKrnibnmrorlrZfnrKY="
}

解码Base64:

@Test
public void base64DecodeTest() {
    String inputString = "QmFzZTY06L6T5Ye65YyF5ZCrK+WSjC/kuKTkuKrnibnmrorlrZfnrKY=";
    System.out.println(new String(Base64.getDecoder().decode(inputString), StandardCharsets.UTF_8));
    // 打印“Base64输出包含+和/两个特殊字符”
}

解码Base64时,Python和Java都同时支持入参为字符串或字节数组。可能是因为Base64字符串总是可以使用ASCII实现字符编码,因此实现了两个重载方法。Java的byte[] decode(String src)重载方法是对byte[] decode(byte[] src)的封装,内部也是将入参src先用字符编码ISO_8859_1(兼容ASCII)转换为字节数组。

编码Base64 URL Safe:

@Test
public void base64UrlSafeEncodeTest() {
    String inputString = "Base64输出包含+和/两个特殊字符";
    byte[] inputStringBytes = inputString.getBytes(StandardCharsets.UTF_8);
    System.out.println(new String(Base64.getUrlEncoder().encode(inputStringBytes), StandardCharsets.US_ASCII));
    // 打印"QmFzZTY06L6T5Ye65YyF5ZCrK-WSjC_kuKTkuKrnibnmrorlrZfnrKY="
}

编解码Base62:https://github.com/seruco/base62

参考资料

https://developer.mozilla.org/en-US/docs/Glossary/Base64
https://en.wikipedia.org/wiki/Base62
https://docs.python.org/3/library/base64.html
https://docs.oracle.com/javase/8/docs/api/java/util/Base64.html