目录
原理
什么是base64
- Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法。
为什么会有base64
- 由于HTTP协议是文本协议,所以在HTTP协议下传输二进制数据需要将二进制数据转换为字符数据。然而直接转换是不行的。因为网络传输只能传输可打印字符。
什么是“可打印字符”呢?
- 在ASCII码中规定,031、128这33个字符属于控制字符,32127这95个字符属于可打印字符,也就是说网络传输只能传输这95个字符,不在这个范围内的字符无法传输。
那么该怎么才能传输其他字符呢?
- 其中一种方式就是使用Base64。Base64一般用于在HTTP协议下传输二进制数据。
可忽略
- 完整的base64定义可见 RFC1421和 RFC2045。编码后的数据比原始数据略长,为原来的4/3。在电子邮件中,根据RFC822规定,每76个字符,还需要加上一个回车换行。可以估算编码后数据长度大约为原长的135.1
Table 1: The Base64 Alphabet
索引 | 对应字符 | 索引 | 对应字符 | 索引 | 对应字符 | 索引 | 对应字符 |
0 | A | 17 | R | 34 | i | 51 | z |
1 | B | 18 | S | 35 | j | 52 | 0 |
2 | C | 19 | T | 36 | k | 53 | 1 |
3 | D | 20 | U | 37 | l | 54 | 2 |
4 | E | 21 | V | 38 | m | 55 | 3 |
5 | F | 22 | W | 39 | n | 56 | 4 |
6 | G | 23 | X | 40 | o | 57 | 5 |
7 | H | 24 | Y | 41 | p | 58 | 6 |
8 | I | 25 | Z | 42 | q | 59 | 7 |
9 | J | 26 | a | 43 | r | 60 | 8 |
10 | K | 27 | b | 44 | s | 61 | 9 |
11 | L | 28 | c | 45 | t | 62 | + |
12 | M | 29 | d | 46 | u | 63 | / |
13 | N | 30 | e | 47 | v | ||
14 | O | 31 | f | 48 | w | ||
15 | P | 32 | g | 49 | x | ||
16 | Q | 33 | h | 50 | y |
- 也就是说,如果将索引转换为对应的二进制数据的话需要至多6个Bit(2^6=64)。然而ASCII码需要8个Bit来表示,那么怎么使用6个Bit来表示8个Bit的数据呢?6个Bit当然不能存储8个Bit的数据,但是4*6个Bit可以存储3*8个Bit的数据啊!
- 可以看到“Son”通过Base64编码转换成了“U29u”。这是刚刚好的情况,3个ASCII字符刚好转换成对应的4个Base64字符。但是,当需要转换的字符数不是3的倍数的情况下该怎么办呢?Base64规定,当需要转换的字符不是3的倍数时,一律采用补0的方式凑足3的倍数,具体如下表所示:
- 每6个Bit为一组,第一组转换后为字符“U”,第二组末尾补4个0转换后为字符“w”。剩下的使用“=”替代。即字符“S”通过Base64编码后为“Uw==”。这就是Base64的编码过程。
HTML规范关于base64有哪些API
以下两个函数只能对 **ASCII码** 进行编解码,使用的是拉丁字符集
提示:
- 拉丁字符集和utf-8前128位字符,都涵盖了ASCII码。所以各种字符集互转,都是将本字符集
window.atob()
-
对base64编码过的字符串进行解码
-
a: ASCII码 b:Binary
-
atob 意思是通过ASCII码形式表示二进制数,
不是将ASCII码转换为二进制数
window.btoa()
- 对ASCII编码的字符串进行base64编码(不支持汉字,汉字可通过URIencode预处理后再编码)
- 将ascii码解析成binary数据
注意
-
标准的Base64并不适合直接放在URL里传输,因为URL编码器会把标准Base64中的“/”和“+”字符变为形如“%XX”的形式,而这些“%”号在存入数据库时还需要再进行转换,因为ANSI SQL中已将“%”号用作通配符。
-
为解决此问题,可采用一种用于URL的改进Base64编码,它在末尾填充**’=’**号,并将标准Base64中的“+”和“/”分别改成了“-”和“_”,这样就免去了在URL编解码和数据库存储时所要作的转换,避免了编码信息长度在此过程中的增加,并统一了数据库、表单等处对象标识符的格式。
实现
Base64实现
JavaScript源码版
1. 源码版
const Base64Util = {
_keyStr: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
encode: function (e) {
let t = ''
let n, r, i, s, o, u, a
let f = 0
e = Base64Util._utf8_encode(e)
while (f < e.length) {
n = e.charCodeAt(f++)
r = e.charCodeAt(f++)
i = e.charCodeAt(f++)
s = n >> 2
o = (n & 3) << 4 | r >> 4
u = (r & 15) << 2 | i >> 6
a = i & 63
if (isNaN(r)) {
u = 64
a = 64
} else if (isNaN(i)) {
a = 64
}
t = t + this._keyStr.charAt(s) + this._keyStr.charAt(o) + this._keyStr.charAt(u) + this._keyStr.charAt(a)
}
return t
},
decode: function (e) {
var t = ''
var n, r, i
var s, o, u, a
var f = 0
// eslint-disable-next-line no-useless-escape
e = e.replace(/[^A-Za-z0-9\+\/\=]/g, '')
while (f < e.length) {
s = this._keyStr.indexOf(e.charAt(f++))
o = this._keyStr.indexOf(e.charAt(f++))
u = this._keyStr.indexOf(e.charAt(f++))
a = this._keyStr.indexOf(e.charAt(f++))
n = s << 2 | o >> 4
r = (o & 15) << 4 | u >> 2
i = (u & 3) << 6 | a
t = t + String.fromCharCode(n)
if (u !== 64) {
t = t + String.fromCharCode(r)
} if (a !== 64) {
t = t + String.fromCharCode(i)
}
}
t = Base64Util._utf8_decode(t)
return t
},
_utf8_encode: function (e) {
e = e.replace(/\r\n/g, 'n')
let t = ''
for (let n = 0; n < e.length; n++) {
let r = e.charCodeAt(n)
// ASCII码
if (r < 128) {
t += String.fromCharCode(r)
} else if (r > 127 && r < 2048) {
t += String.fromCharCode(r >> 6 | 192)
t += String.fromCharCode(r & 63 | 128)
} else {
t += String.fromCharCode(r >> 12 | 224)
t += String.fromCharCode(r >> 6 & 63 | 128)
t += String.fromCharCode(r & 63 | 128)
}
}
return t
},
_utf8_decode: function (e) {
var t = ''
var n = 0
var r = 0
var c1 = 0
var c2 = 0
while (n < e.length) {
r = e.charCodeAt(n)
if (r < 128) {
t += String.fromCharCode(r)
n++
} else if (r > 191 && r < 224) {
c1 = e.charCodeAt(n + 1)
t += String.fromCharCode((r & 31) << 6 | c1 & 63)
n += 2
} else {
c1 = e.charCodeAt(n + 1)
c2 = e.charCodeAt(n + 2)
t += String.fromCharCode((r & 15) << 12 | (c1 & 63) << 6 | c2 & 63)
n += 3
}
}
return t
}
}
2. api版 不推荐,
// 使用utf-8字符集进行base64编码
function utoa(str) {
// 1. encodeURIComponent只能将中文加密,不可以加密ascii, 将utf-8字符串 转化为 16进制字符串
// 2. unescape将 16进制序列 转换为 拉丁字符集
// 3. btoa 将拉丁字符集 用base64 编码
return btoa(unescape(encodeURIComponent(str)));
}
// 使用utf-8字符集解析base64字符串
function atou(str) {
// 1. atob 将 base64编码 转化为 拉丁字符集
// 2. unescape将 拉丁字符集 转换为 16进制序列
// 3. decodeURIComponent只能将中文解密,不可以加密ascii, 将16进制字符串 转化为 utf-8字符集
return decodeURIComponent(escape(window.atob(str)));
}
java版
import java.util.Base64;
对于标准的Base64:
加密为字符串使用Base64.getEncoder().encodeToString();
加密为字节数组使用Base64.getEncoder().encode();
解密使用Base64.getDecoder().decode();
对于URL安全或MIME的Base64,只需将上述getEncoder()getDecoder()更换为getUrlEncoder()getUrlDecoder()
或getMimeEncoder()和getMimeDecoder()即可。
应用场景
base64有哪些应用场景
- 前端将较小的icon编码为base64直接在文档中加载,减少http请求次数
- 电子邮件传输二进制文件时,通常用base64编码后再传
注意:
- base64编码后的数据量是要比编码前大的,所以base64不能用于减少数据量。
- base64不能用于加密数据,即使使用私有的索引表也是不安全的,只是一种编码方式。