有时,在实际项目中,需要将图片编码为Base64格式,然后进行传送。比如,腾讯云中一些实名认证的服务,就需要将图片编码为Base64格式,然后调用API接口进行验证。 那到底什么是Base64呢?
一、Base64
百度百科解释是:Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法。
什么是“可打印字符”呢?为什么要用它来传输8Bit字节码呢?
在回答这两个问题之前我们有必要来思考一下什么情况下需要使用到Base64?Base64一般用于在HTTP协议下传输二进制数据,由于HTTP协议是文本协议,所以在HTTP协议下传输二进制数据需要将二进制数据转换为字符数据。然而直接转换是不行的。因为网络传输只能传输可打印字符。什么是可打印字符?在ASCII码中规定,0~31、127这33个字符属于控制字符,32~126这95个字符属于可打印字符,也就是说网络传输只能传输这95个字符,不在这个范围内的字符无法传输。那么该怎么才能传输其他字符呢?其中一种方式就是使用Base64。
Base64,就是使用64个可打印字符来表示二进制数据的方法。Base64的索引与对应字符的关系如下表所示:
但是,标准的Base64并不适合直接放在URL里传输,因为URL编码器会把标准Base64中的“/”和“+”字符变为形如“%XX”的形式,而这些“%”号在存入数据库时还需要再进行转换,因为ANSI SQL中已将“%”号用作通配符。
为了解决这个问题,人们提出了一些Base64的变种。其主要思路就是将“/”和“+”用其他的字符进行替换,从而避免出现歧义。
其中,一种用于URL的改进Base64编码,它在末尾填充’='号,并将标准Base64中的“+”和“/”分别改成了“-”和“_”。
另外一种用于正则表达式的改进Base64变种,它将“+”和“/”改成了“!”和“-”。
此外还有一些变种,它们将“+“、”/”改为“_-”或“._”(用作编程语言中的标识符名称)或“.-”(用于XML中的Nmtoken)甚至“_:”(用于XML中的Name)。
二、编码过程
也就是说,如果将索引转换为对应的二进制数据的话需要至多6个Bit。然而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的编码过程。
Base64要求把每三个8Bit的字节转换为四个6Bit的字节(3*8 = 4*6 = 24),然后把6Bit再添两位高位0,组成四个8Bit的字节,也就是说,转换后的字符串理论上将要比原来的长1/3。
三、 解码过程
理解了编码过程以后,解码过程就比较简单了,就是将编码过程倒过来解析一下Base64编码即可。
当Base64编码中不存在 = 时,也就是说恰好可以进行解码时,解码过程如下:
当存在 = 时,解码过程如下,就是直接忽略等号,只对有效字符进行解码,
这就是Base64的解码过程。
四、Java中实现Base64
import org.junit.Test;
import java.io.UnsupportedEncodingException;
import java.util.Base64;
public class Test {
@Test
public void test() throws UnsupportedEncodingException {
// 编码
String encode = Base64.getEncoder().encodeToString("So".getBytes("UTF-8"));
System.out.println(encode);
// 解码
byte[] decode = Base64.getDecoder().decode(encode);
System.out.println(new String(decode, "UTF-8"));
}
}
import java.util.Base64;
对于标准的Base64:
加密为字符串使用Base64.getEncoder().encodeToString();
加密为字节数组使用Base64.getEncoder().encode();
解密使用Base64.getDecoder().decode();
对于URL安全或MIME的Base64,只需将上述getEncoder()getDecoder()更换为getUrlEncoder()getUrlDecoder()
或getMimeEncoder()和getMimeDecoder()即可。
五、图片和Base64编码的互转
图片本身也是二进制的数据,所以同样可以进行Base64的编码解码。
除了java中自带的工具外,在java的Spring框架中提供了Base64编码解码的工具。
将本地图片转换成Base64编码:
import org.apache.http.util.TextUtils;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;
import sun.misc.BASE64Encoder;
import java.io.*;
public static String imageToBase64(String path){
if(TextUtils.isEmpty(path)){
return null;
}
InputStream is = null;
byte[] data = null;
String result = null;
try{
is = new FileInputStream(path);
//创建一个字符流大小的数组。
data = new byte[is.available()];
//写入数组
is.read(data);
//用默认的编码格式进行编码
result = Base64Utils.encodeToString(data);
System.out.println(result);
}catch (IOException e){
e.printStackTrace();
}finally {
if(null !=is){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return result;
}
将在线图片转成Base64编码:
import org.apache.http.util.TextUtils;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;
import sun.misc.BASE64Encoder;
import java.io.*;
public static String ImageToBase64ByOnline(String imgURL) {
ByteArrayOutputStream data = new ByteArrayOutputStream();
try {
// 创建URL
URL url = new URL(imgURL);
byte[] by = new byte[1024];
// 创建链接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
InputStream is = conn.getInputStream();
// 将内容读取内存中
int len = -1;
while ((len = is.read(by)) != -1) {
data.write(by, 0, len);
}
//关闭流
is.close();
} catch (IOException e) {
e.printStackTrace();
}
return Base64Utils.encodeToString(data.toByteArray());
}
将Base64编码解析为图片:
public static boolean Base64ToImage(String imgStr,String imgFilePath) {
if (StringUtils.isEmpty(imgStr)) // 图像数据为空
return false;
try {
// Base64解码
byte[] b = Base64Utils.decodeFromString(imgStr);
for (int i = 0; i < b.length; ++i) {
if (b[i] < 0) {// 调整异常数据
b[i] += 256;
}
}
OutputStream out = new FileOutputStream(imgFilePath);
out.write(b);
out.flush();
out.close();
return true;
} catch (Exception e) {
return false;
}
}
六、其他语言中的Base64编解码
百度百科中列举了很多种编程语言的Base64编码解码方法,请见参考链接3。
参考链接:
1、base64是什么?
2、图片和base64之间的互相转换
3、base64----百度百科