Java 8新特性探究(十一)Base64詳解
BASE64 編碼是一種常用的字符編碼,在很多地方都會用到。但base64不是安全領域下的加密解密算法。能起到安全作用的效果很差,而且很容易破解,他核心作用應該是傳輸數據的正確性,有些網關或系統只能使用ASCII字符。Base64就是用來將非ASCII字符的數據轉換成ASCII字符的一種方法,而且base64特別適合在http,mime協議下快速傳輸數據。
JDK裏面實現Base64的API
在JDK1.6之前,JDK核心類一直沒有Base64的實現類,有人建議用Sun/Oracle JDK裏面的sun.misc.BASE64Encoder 和 sun.misc.BASE64Decoder,使用它們的優點就是不需要依賴第三方類庫,缺點就是可能在未來版本會被刪除(用maven編譯會發出警告),而且性能不佳,後面會有性能測試。
JDK1.6中添加了另一個Base64的實現,javax.xml.bind.DatatypeConverter兩個靜態方法parseBase64Binary 和 printBase64Binary,隱藏在javax.xml.bind包下面,不被很多開發者知道。
在Java 8在java.util包下面實現了BASE64編解碼API,而且性能不俗,API也簡單易懂,下面展示下這個類的使用例子。
java.util.Base64
該類提供了一套靜態方法獲取下面三種BASE64編解碼器:
1)Basic編碼:是標準的BASE64編碼,用於處理常規的需求
1
2
3
4
5
6
|
//
編碼 String
asB64 = Base64.getEncoder().encodeToString( "some
string" .getBytes( "utf-8" )); System.out.println(asB64);
//
輸出爲: c29tZSBzdHJpbmc= //
解碼 byte []
asBytes = Base64.getDecoder().decode( "c29tZSBzdHJpbmc=" ); System.out.println( new
String(asBytes, "utf-8" ));
//
輸出爲: some string |
2)URL編碼:使用下劃線替換URL裏面的反斜線“/”
1
2
3
4
|
String
urlEncoded = Base64.getUrlEncoder().encodeToString( "subjects?abcd" .getBytes( "utf-8" )); System.out.println( "Using
URL Alphabet: "
+ urlEncoded); //
輸出爲: Using
URL Alphabet: c3ViamVjdHM_YWJjZA== |
3)MIME編碼:使用基本的字母數字產生BASE64輸出,而且對MIME格式友好:每一行輸出不超過76個字符,而且每行以“\r\n”符結束。
1
2
3
4
5
6
7
|
StringBuilder
sb = new
StringBuilder(); for
( int
t = 0 ;
t < 10 ;
++t) { sb.append(UUID.randomUUID().toString()); } byte []
toEncode = sb.toString().getBytes( "utf-8" ); String
mimeEncoded = Base64.getMimeEncoder().encodeToString(toEncode); System.out.println(mimeEncoded); |
第三方實現Base64的API
首先便是常用的Apache Commons Codec library裏面的org.apache.commons.codec.binary.Base64;
第二個便是Google Guava庫裏面的com.google.common.io.BaseEncoding.base64() 這個靜態方法;
第三個是net.iharder.Base64,這個jar包就一個類;
最後一個,號稱Base64編碼速度最快的MigBase64,而且是10年前的實現,到現在是否能保持這個稱號,測一測便知道;
Base64編碼性能測試
上面講了一共7種實現Base64編碼,Jdk裏面3種,第三方實現4種,一旦有選擇,則有必要將他們進行一次高低對比,性能測試是最直接的方式
首先來定義兩個接口
1
2
3
4
5
6
7
8
9
10
|
private
static
interface
Base64Codec { public
String encode( final
byte []
data); public
byte []
decode( final
String base64) throws
IOException; } private
static
interface
Base64ByteCodec { public
byte []
encodeBytes( final
byte []
data); public
byte []
decodeBytes( final
byte []
base64) throws
IOException; } |
兩個接口區別就是其中一個接口方法參數接收byte數組,返回byte數組,因爲byte->byte相比String->byte或者byte->String性能上會快一點,所以區分兩組來測試
1
2
3
4
|
private
static
final
Base64Codec[] m_codecs = { new
GuavaImpl(), new
JavaXmlImpl(), new
Java8Impl(), new
SunImpl(), new
ApacheImpl(), new
MiGBase64Impl(), new
IHarderImpl() }; private
static
final
Base64ByteCodec[] m_byteCodecs = { new
ApacheImpl(), new
Java8Impl(), new
MiGBase64Impl(), new
IHarderImpl() }; |
從上面看出,其中支持byte->byte只有4中API;
7個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
|
private
static
class
Java8Impl implements
Base64Codec, Base64ByteCodec { private
final
Base64.Decoder m_decoder = Base64.getDecoder(); private
final
Base64.Encoder m_encoder = Base64.getEncoder(); @Override public
String encode( byte []
data) { return
m_encoder.encodeToString(data); } @Override public
byte []
decode(String base64) throws
IOException { return
m_decoder.decode(base64); } public
byte []
encodeBytes( byte []
data) { return
m_encoder.encode( data ); } public
byte []
decodeBytes( byte []
base64) throws
IOException { return
m_decoder.decode( base64 ); } } private
static
class
JavaXmlImpl implements
Base64Codec //no
byte[] implementation { public
String encode( byte []
data) { return
DatatypeConverter.printBase64Binary( data ); } public
byte []
decode(String base64) throws
IOException { return
DatatypeConverter.parseBase64Binary( base64 ); } } .............. |
後面代碼基本就是各種API實現Base64的代碼了,就不詳細列出。
主要測試手段是,生成100M的隨機數,分成100byte或者1000byte的塊,然後將他們分別編碼和解碼,記錄時間,如下方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
private
static
TestResult testByteCodec( final
Base64ByteCodec codec, final
List< byte []>
buffers ) throws
IOException { final
List< byte []>
encoded = new
ArrayList< byte []>(
buffers.size() ); final
long
start = System.currentTimeMillis(); for
( final
byte []
buf : buffers ) encoded.add(
codec.encodeBytes(buf) ); final
long
encodeTime = System.currentTimeMillis() - start; final
List< byte []>
result = new
ArrayList< byte []>(
buffers.size() ); final
long
start2 = System.currentTimeMillis(); for
( final
byte []
ar : encoded ) result.add(
codec.decodeBytes(ar) ); final
long
decodeTime = System.currentTimeMillis() - start2; for
( int
i = 0 ;
i < buffers.size(); ++i ) { if
( !Arrays.equals( buffers.get( i ), result.get( i ) ) ) System.out.println(
"Diff
at pos = "
+ i ); } return
new
TestResult( encodeTime / 1000.0 ,
decodeTime / 1000.0
); } |
測試結果
jvm參數:-Xms512m -Xmx4G
一切都很明顯了,從上面看出,sun的表現不是很好,IHarder和MigBase64性能可以接受,傳說MigBase64性能第一,那也是過去了,在這次測試結果中,新的java8 base64運行速度最好,javaXml表現次之。
總結
如果你需要一個性能好,可靠的Base64編解碼器,不要找JDK外面的了,java8裏面的java.util.Base64以及java6中隱藏很深的javax.xml.bind.DatatypeConverter,他們兩個都是不錯的選擇。