Base64(基底64)是一种基于64个可打印字符来表示二进制数据的表示方法。每6个比特为一个单元,对应某个可打印字符。3个字节相当于24个比特,对应于4个Base64单元,即3个字节可由4个可打印字符来表示。在Base64中的可打印字符包括字母A-Z
、a-z
、数字0-9
,这样共有62个字符,此外两个可打印符号在不同的系统中而不同。一些如uuencode的其他编码方法,和之后BinHex的版本使用不同的64字符集来代表6个二进制数字,但是不被称为Base64。
Base64常用于在通常处理文本数据的场合,表示、传输、存储一些二进制数据,包括MIME的电子邮件及XML的一些复杂数据。
举例来说,一段引用自托马斯·霍布斯《利维坦》的文句:
Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.
经过Base64编码之后变成:
TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=
编码“Man”的结果为TWFu
,详细原理如下:
文本 | M | a | n | |||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ASCII编码 | 77 | 97 | 110 | |||||||||||||||||||||
二进制位 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 1 | 0 |
索引 | 19 | 22 | 5 | 46 | ||||||||||||||||||||
Base64编码 | T | W | F | u |
简单来说Base64就是将一个字符串转换成二进制然后将8位分割成6位,再找到Base64索引表中对应的字符,如果要编码的字节数不能被三整除,那么在末尾补上0,最后转换成Base64时以=表示。参考下表:
文本(1 Byte) | A | |||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
二进制位 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
二进制位(补0) | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Base64编码 | Q | Q | = | = | ||||||||||||||||||||
文本(2 Byte) | B | C | ||||||||||||||||||||||
二进制位 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
二进制位(补0) | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Base64编码 | Q | k | M | = |
那么我们来用代码实现一下Base64的编码和解码:
首先根据上面的原理我们知道Base64的编码会需要一个对应表和二进制和十进制的相互转换。
先将Base64的对应表变成一个字符数组。
private static final char[] toBase64 = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
};
然后写二进制和十进制转换的方法。这里我们写一个二进制转换成十进制的方法,而十进制转二进制我们用Intgrer对象的方法来实现。
public static int BinaryToDecimal(int binaryNumber) {
//二进制转十进制
int decimal = 0;
int p = 0;
while (true) {
if (binaryNumber == 0) {
break;
} else {
int temp = binaryNumber % 10;
decimal += temp * Math.pow(2, p);
binaryNumber = binaryNumber / 10;
p++;
}
}
return decimal;
}
我们先将一个字符串传过来,然后将它变成一个字节数组,注意因为中文的问题,这里转换成字符数组会出现问题,所以我们使用字节数组,然后对其进行判断,是不是3的倍数,需要补几个0,补完之后转换成二进制,转换成二进制之后再按每6位数得到一个新的二位数,根据这个新的二位数的十进制值去找到Base64对应表数组里对应的值,最后根据补0的数在编码后补上=,就完成了一个字符串的Base64编码。
//base64编码
public static String geyBase64(String str) throws UnsupportedEncodingException {
if (str.getBytes().length % 3 == 1) {
str = str + "00";
byte[] b = str.getBytes();
b[b.length - 1] = 0;
b[b.length - 2] = 0;
String result = "";
for (int i = 0; i < b.length; i++) {
result += Integer.toBinaryString((b[i] & 0xFF) + 0x100).substring(1);
}
String str1 = "";
for (int i = 0; i < result.length() - 12; i = i + 6) {
str1 += String.valueOf(toBase64[BinaryToDecimal(Integer.parseInt(result.substring(i, i + 6)))]);
}
str1 = str1 + "==";
return str1;
} else if (str.getBytes().length % 3 == 2) {
str = str + "0";
byte[] b = str.getBytes();
b[b.length - 1] = 0;
String result = "";
for (int i = 0; i < b.length; i++) {
result += Integer.toBinaryString((b[i] & 0xFF) + 0x100).substring(1);
}
String str1 = "";
for (int i = 0; i < result.length() - 6; i = i + 6) {
str1 += String.valueOf(toBase64[BinaryToDecimal(Integer.parseInt(result.substring(i, i + 6)))]);
}
str1 = str1 + "=";
return str1;
} else if (str.getBytes().length % 3 == 0) {
byte[] b = str.getBytes();
String result = "";
for (int i = 0; i < b.length; i++) {
result += Integer.toBinaryString((b[i] & 0xFF) + 0x100).substring(1);
}
String str1 = "";
for (int i = 0; i < result.length(); i = i + 6) {
str1 += String.valueOf(toBase64[BinaryToDecimal(Integer.parseInt(result.substring(i, i + 6)))]);
}
return str1;
}
return "0";
}
而解码的过程就是上面的逆向操作。根据字符找到对应表数组的下标,将下标转换成二进制,再将6位的二进制重新取到8位,将其重新转换回字节数组,然后转成字符串。
public static String getBase64String(String base) throws UnsupportedEncodingException {
String result = "";
base=base.replace('=','A');
for (int i = 0; i < base.length(); i++) {
for (int j = 0; j < toBase64.length - 1; j++) {
if (base.charAt(i) == toBase64[j]) {
result += Integer.toBinaryString((j & 0xFF) + 0x100).substring(3);
}
}
}
byte[] b= new byte[result.length()/8];
for (int k = 0; k < result.length(); k = k + 8) {
b[k/8]=(byte) BinaryToDecimal(Integer.parseInt(result.substring(k, k + 8)));
}
String str = new String(b);
return str;
}
测试一下结果
public static void main(String[] args) throws UnsupportedEncodingException {
String str = "今天是个好日子";
String str2 = geyBase64(str);
System.out.println(str2);
String str3 =getBase64String(str2);
System.out.println(str3);
}
可以看到我们的程序编码之后和网站上的结果是一样的。解码也成功得到了原来的字符串。