Base64编码的原理与实现

在数据传输的时候,并不是所有的字符都可以受到支持,很多时候只能传输可见字符,对于不可见字符的传输需要经过特殊处理,这就是Base64产生的原因。Base64编码是一种基于64个可打印字符来表示二进制数据的表示方法。由于$2^6=64$,所以每6个比特为一个单元,对应某个可打印字符。3个字节有24个比特,对应于4个Base64单元,即3个字节可由4个可打印字符来表示。在Base64中的可打印字符包括字母A-Z、a-z、数字0-9,这样共有62个字符,此外两个可打印符号在不同的系统中而不同。一些如uuencode的其他编码方法,和之后BinHex的版本使用不同的64字符集来代表6个二进制数字,但是不被称为Base64。
Base64常用于在通常处理文本数据的场合,表示、传输、存储一些二进制数据,包括MIME的电子邮件及XML的一些复杂数据。用Base64编码图片也是互联网上常见的做法。

Base64的字符索引如下表所示:

数值 | 字符 | 数值 | 字符 | 数值 | 字符 | 数值 | 字符

  • | - | - | - | - | - | -
    0 | A | 16 | Q | 32 | g | 48 | w
    1 | B | 17 | R | 33 | h | 49 | x
    2 | C | 18 | S | 34 | i | 50 | y
    3 | D | 19 | T | 35 | j | 51 | z
    4 | E | 20 | U | 36 | k | 52 | 0
    5 | F | 21 | V | 37 | l | 53 | 1
    6 | G | 22 | W | 38 | m | 54 | 2
    7 | H | 23 | X | 39 | n | 55 | 3
    8 | I | 24 | Y | 40 | o | 56 | 4
    9 | J | 25 | Z | 41 | p | 57 | 5
    10 | K | 26 | a | 42 | q | 58 | 6
    11 | L | 27 | b | 43 | r | 59 | 7
    12 | M | 28 | c | 44 | s | 60 | 8
    13 | N | 29 | d | 45 | t | 61 | 9
    14 | O | 30 | e | 46 | u | 62 | +
    15 | P | 31 | f | 47 | v | 63 | /

这是一个根据上表进行编码的例子:

如果要编码的字节数不能被3整除,最后会多出1个或2个字节,那么可以使用下面的方法进行处理:先使用0字节值在末尾补足,使其能够被3整除,然后再进行Base64的编码。在编码后的Base64文本后加上一个或两个=号,代表补足的字节数。也就是说,当最后剩余两个八位字节(2个byte)时,最后一个6位的Base64字节块有四位是0值,最后附加上两个等号;如果最后剩余一个八位字节(1个byte)时,最后一个6位的Base字节块有两位是0值,最后附加一个等号。可以参考下面的例子:

这是一个用Python实现Base64编码的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#!/usr/bin/env python3

def myBase64Encode(preCoding):
charTable = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' #字符表

if len(preCoding) < 0:
return '' #字符串为空则返回空
lackCharNums = 3 - len(preCoding) % 3
if lackCharNums == 3 : lackCharNums = 0 #整除说明不缺字符
#待转换字符不是3的倍数的情况补全它
for i in range(lackCharNums) :
preCoding = preCoding + b'\x00'
result = '' #用于保存最终结果的str数据
rp = '' #处理补全字符时的暂存变量
#每三个字符处理一轮
for i in range(int(len(preCoding) / 3)):
threeChar = preCoding[i * 3 : i * 3 + 3] #取三个字符出来
tCode = '' #用于存放三个字符拼接后的二进制数值 文本形式
pCode = '' #暂存变量
for j in range(3) :
pCode = bin(threeChar[j])[2 :] #把省略的0补上
lackZeroNums = 8 - len(pCode) #省略的0的个数
for x in range(lackZeroNums):
pCode = '0' + pCode
tCode = tCode + pCode
pCode = ''
for j in range(4): #每6位一个字符
pCode = tCode[j * 6 : j * 6 + 6]
rp = rp + charTable[int(pCode, 2)]
#处理补全的00字符
result = rp[: len(rp) - lackCharNums]
for j in range(lackCharNums):
result = result + '='
return bytes(result, encoding = "utf-8")

def myBase64Decode(encodedBin) :
charTable = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' #字符表

#如果字符不是4的倍数 返回空
if not len(encodedBin) % 4 == 0 :
return ''

tCode = '' #用于存放最终的二进制文本字符串
pCpde = '' #暂存变量
#遍历encodedBin每一个字符
for i in encodedBin:
for j in range(len(charTable)): #找到表中对应坐标
if chr(i) == charTable[j]:
pCode = bin(j)[2 :] #转二进制去除开头的0b
lackZeroNums = 6 - len(pCode) #省略的0的个数
for x in range(lackZeroNums):
pCode = '0' + pCode
tCode = tCode + pCode
pCode = ''
result = '' #储存最终结果
for i in range(int(len(tCode) / 8)):
pCode = tCode[i * 8 : i * 8 + 8]
result = result + chr(int(pCode, 2))
return bytes(result, encoding = "utf-8")

print(myBase64Encode(b"helloworld"))
print(myBase64Decode(b"aGVsbG93b3JsZA=="))

浏览器中可以直接调用相关的API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>文件base64——直接拖进来</title>
</head>
<style>
body {
word-break: break-all;
}
</style>
<body>
<script>
window.addEventListener("dragenter", function(event) {
event.preventDefault();
}, false);
window.addEventListener("dragover", function(event) {
event.preventDefault();
}, false);
window.addEventListener("drop", function(event) {
var reader = new FileReader();
reader.onload = function(e) {
document.body.insertAdjacentHTML("afterBegin", '<p>' + e.target.result + '</p>');
//base64 encoded file data!
};
reader.readAsDataURL(event.dataTransfer.files[0]);
event.preventDefault();
}, false);
</script>
</body>
</html>

这是加载Base64编码的图片的方法。这可以将图片硬编码到网页中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Base64 Encode编码实现1x1px透明(黑白)图片</title>
<style>
body {
text-align: center;
}
</style>
</head>
<body>
<h1>以下是透明图片</h1>
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="500" height="100" />
<h1>以下是黑色图片</h1>
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=" width="500" height="100" />
</body>
</html>

拓展阅读:
Base64 - 维基百科
Base64编码原理及脚本的简单实现

🍭支持一根棒棒糖!
0%