【Base64】JDK裏面實現Base64的API

原文出處: 成熟的毛毛蟲的博客html

BASE64 編碼是一種經常使用的字符編碼,在不少地方都會用到。但base64不是安全領域下的加密解密算法。能起到安全做用的效果不好,並且很容易破解,他核心做用應該是傳輸數據的正確性,有些網關或系統只能使用ASCII字符。Base64就是用來將非ASCII字符的數據轉換成ASCII字符的一種方法,並且base64特別適合在http,mime協議下快速傳輸數據。java

JDK裏面實現Base64的API

在JDK1.6以前,JDK核心類一直沒有Base64的實現類,有人建議用Sun/Oracle JDK裏面的sun.misc.BASE64Encoder 和 sun.misc.BASE64Decoder,使用它們的優勢就是不須要依賴第三方類庫,缺點就是可能在將來版本會被刪除(用maven編譯會發出警告),並且性能不佳,後面會有性能測試。git

JDK1.6中添加了另外一個Base64的實現,javax.xml.bind.DatatypeConverter兩個靜態方法parseBase64Binary 和 printBase64Binary,隱藏在javax.xml.bind包下面,不被不少開發者知道。算法

在Java 8在java.util包下面實現了BASE64編解碼API,並且性能不俗,API也簡單易懂,下面展現下這個類的使用例子。apache

java.util.Base64數組

該類提供了一套靜態方法獲取下面三種BASE64編解碼器:安全

1)Basic編碼:是標準的BASE64編碼,用於處理常規的需求app

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裏面的反斜線「/」dom

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」符結束。jvm

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,他們兩個都是不錯的選擇。

本篇中全部代碼都在http://git.oschina.net/benhail/javase8-sample ,歡迎你們去關注下載

相關文章
相關標籤/搜索