base64:因为HTTP协议是文本协议,传输信息时只支持可打印字符,所以为了使HTTP协议能够支持传输图片、文件等等数据,只能先将数据转换成base64的字符串来保存。
可打印字符:ASCII码中一共有128个字符(0-127),其中只有32-126这95个字符属于可打印字符,base64选取的64个字符是A-Z,a-z,0-9,+ 和 /。如下所示,1就是B,63就是/,另外还有一个 = 号用于标识结尾(不一定都有,后面会解释)。
/**
* This array is a lookup table that translates 6-bit positive integer
* index values into their "Base64 Alphabet" equivalents as specified
* in "Table 1: The Base64 Alphabet" of RFC 2045 (and RFC 4648).
*/
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', '+', '/'
};
原理:算法将这64个字符通过6位来一一对应起来,表示为000000-111111。然后将三个字节的数据拆分成4份分别使用这64个字符来表示。也就是说通过base64加密后,每三个字符都会最终变成四个字符。
使用的是java.util.Base64提供的方法,加解密分别对应的是Base64中的两个内部类,需要jdk版本至少大于等于1.8。
加密:
//从流中获取到的字节数组
byte[] bytes = new byte[3072];
Base64.getEncoder().encodeToString(bytes);
解密:
//使用base64将流加密之后的字符串
StringBuilder encode = new StringBuilder();
Base64.getDecoder().decode(encode.toString());
public static void main(String[] args) {
long t1 = System.currentTimeMillis();
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream("F:\\1.pdf")); //读取数据
bos = new BufferedOutputStream(new FileOutputStream("F:\\6.pdf")); //写数据
byte[] bytes = new byte[3072]; //字节数组
byte[] decode;
StringBuilder encode = new StringBuilder();
while (bis.read(bytes) != -1) { //读取到字节数组中
System.out.println("读取...");
encode.append(Base64.getEncoder().encodeToString(bytes)); //每次将加密后的字符串追加到encode中
}
decode = Base64.getDecoder().decode(encode.toString()); //将encode解密
bos.write(decode); //写到文件中
System.out.println("end...");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bis != null) {
bis.close();
}
if (bos != null) {
bos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
long t2 = System.currentTimeMillis();
System.out.println(t2 - t1);
}
比如最后只剩下一个字节了。base64首先将后面两个字节全部使用0来代替,将这个字节前6位放入一个空间,后两位放入一个空间,然后低位补0。并且使用 = 号来标识,如果刚好是3的倍数的话就不需要补0操作,自然也就不会有 = 号。
一个字节
11111111
先补成三个字节
11111111 00000000 00000000
分割成四个6位的字符
111111 110000 000000 000000
63 48 0 0
加密后的结果
\w==
可以发现,上面的简单测试用例就是使用的分批加密,然后统一解密的方式。主要为了防止文件过大,转换成byte数组后超出了常数的大小而报错。而使用分批加密必须要注意的点就是需要以3的倍数来获取流的数据,否则加密出来的结果带有=号,在解密时会出现解密出错的情况。
byte[] bytes = new byte[3072]; //每次取用的大小必须是3的倍数
如果不是分批读取的话,是不会存在这种情况的,查看是否对文件的写操作是追加情况。
如果是分批操作,如果数据源是使用多个字节代替一位字符的情况,就有可能取数据的时候取得不完整而导致加解密后得大小不一致,推荐使用1024的倍数来取数据。