轉(zhuǎn)載Java技術(shù) | 細(xì)談 Java 8 中的 Base64
閱讀目錄(Content)
簡要了解
Base 64是一種使64個(gè)字符來表示任意二進(jìn)制數(shù)據(jù)的方法震缭。Base 64是一種非常常用的二進(jìn)制編解碼方案嘶窄。
有些說法是說哭廉,使用Base 64作為加密解密的功能脊僚,比如一個(gè)Web系統(tǒng)中,密碼字段存入數(shù)據(jù)庫的時(shí)候使用Base64.encode
一下,其實(shí)Base 64的編碼辽幌、解碼方法都是簡單公開的增淹,用以加密、解密實(shí)在談不上啥保障性乌企。
在Java 8中虑润,整合了Base 64。Java 8 為開發(fā)者提供了 java.util.Base64
的工具類加酵,Java 8現(xiàn)在有內(nèi)置編碼器和解碼器的Base 64編碼拳喻。在Java 8中有三種類型的Base 64編碼。
第一種猪腕,簡單Base 64
Basic 編碼是標(biāo)準(zhǔn)的Base 64編碼冗澈,用于處理常規(guī)的需求:輸出的內(nèi)容不添加換行符,而且輸出的內(nèi)容由64個(gè)基本字符組成陋葡。
輸出映射設(shè)置字符在A-ZA-Z0-9+/
亚亲。編碼器不添加任何換行輸出和解碼器拒絕在A-Za-z0-9+/
以外的任何字符。
第二種腐缤,URL編碼
輸出映射設(shè)置字符在A-Za-z0-9-_
捌归。輸出URL和文件名安全。由于標(biāo)準(zhǔn)的Basic編碼可能會(huì)出現(xiàn)+
和/
,在URL中就不能直接作為參數(shù)岭粤,所以又有一種“url safe” 的Base 64編碼惜索,其實(shí)就是吧字符+
和/
分別變成-
和_
。
第三種剃浇,MIME編碼
輸出映射到MIME友好的格式巾兆。輸出表示在每次不超過76個(gè)字符行和使用'\r'后跟一個(gè)換行符'\n'回車作為行分隔符。無行隔板的存在是為了使編碼的結(jié)束輸出偿渡。MIME(Multipurpose Internet Mail Extensions)多用途互聯(lián)網(wǎng)郵件擴(kuò)展類型。是設(shè)定某種擴(kuò)展名的文件用對(duì)應(yīng)的應(yīng)用程序來打開的方式類型霸奕。
它是一種互聯(lián)網(wǎng)標(biāo)準(zhǔn)溜宽,擴(kuò)展了電子郵件標(biāo)準(zhǔn),使其可以支持:非ASCII字符文本质帅;非文本格式附件(二進(jìn)制适揉、聲音、圖像等)煤惩;由多部分組成的消息體嫉嘀;包含非ASCII字符的頭信息等。
舉個(gè)栗子:右鍵中有一個(gè)Word附件(二進(jìn)制文件)魄揉,點(diǎn)擊預(yù)覽剪侮,瀏覽器會(huì)直接打開Office。可以理解為MIME設(shè)定了這種對(duì)應(yīng)關(guān)系瓣俯。
MIME編碼器會(huì)使用基本的字母數(shù)字產(chǎn)生Base 64輸出杰标,而且對(duì)MIME格式友好:每一行輸出不超過76個(gè)字符,而且每行以“\r\n”符結(jié)束彩匕。
實(shí)用API與代碼示例
API
Base 64類
序號(hào) | 方法 | 描述 |
---|---|---|
1 | Base64.getEncoder() | 返回一個(gè) Base64.Encoder 腔剂,編碼使用基本型 base64 編碼方案 |
2 | Base64.getDecoder() | 返回一個(gè) Base64.Decoder ,解碼使用基本型 base64 編碼方案 |
3 | Base64. getUrlEncoder() | 返回一個(gè) Base64.Encoder 驼仪,編碼使用 URL 和文件名安全型 base64 編碼方案 |
4 | Base64.getUrlDecoder() | 返回一個(gè) Base64.Decoder 掸犬,解碼使用 URL 和文件名安全型 base64 編碼方案。 |
5 | Base64.getMimeEncoder() | 返回一個(gè) Base64.Encoder 绪爸,編碼使用 MIME 型 base64 編碼方案湾碎。 |
6 | Base64.getMimeEncoder(int lineLength, byte[] lineSeparator) | 返回一個(gè) Base64.Encoder ,編碼使用 MIME 型 base64 編碼方案毡泻,可以通過參數(shù)指定每行的長度及行的分隔符胜茧。 |
7 | Base64.getMimeDecoder() | 返回一個(gè) Base64.Decoder ,解碼使用 MIME 型 base64 編碼方案仇味。 |
內(nèi)嵌類Encoder
序號(hào) | 方法名 | 描述 |
---|---|---|
1 | byte[] encode(byte[] src) | 編碼呻顽,返回一個(gè)byte數(shù)組 |
2 | int encode(byte[] src, byte[] dst) | 編碼,寫進(jìn)新的byte數(shù)組 |
3 | String encodeToString(byte[] src) | 編碼丹墨,返回一個(gè)字符串 |
4 | ByteBuffer encode(ByteBuffer buffer) | 編碼 |
5 | OutputStream wrap(OutputStream os) | 編碼 |
6 | Encoder withoutPadding() | 編碼 |
內(nèi)嵌類Decoder
序號(hào) | 方法名 | 描述 |
---|---|---|
1 | byte[] decode(byte[] src) | 解碼廊遍,返回一個(gè)byte數(shù)組,入?yún)閎yte數(shù)組 |
2 | byte[] decode(String src) | 解碼贩挣,返回一個(gè)byte數(shù)組喉前,入?yún)樽址?/td> |
3 | int decode(byte[] src, byte[] dst) | 解碼 |
4 | ByteBuffer decode(ByteBuffer buffer) | 解碼 |
5 | InputStream wrap(InputStream is) | 解碼 |
代碼示例
package com.kjgym.common.java8;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
/**
* @author kjgym
* @description Demo for Java8's Base64
* @date 2019/9/17 11:25
*/
public class Java8Base642 {
public static void main(String[] args) {
final String demoStr = "kjgym";
final byte[] strByte = demoStr.getBytes(StandardCharsets.UTF_8);
// 基本型編碼方案
String s = Base64.getEncoder().encodeToString(strByte);
System.out.println("基本編碼: " + s);
String ss = new String(Base64.getDecoder().decode(s));
System.out.println("解碼: " + ss);
// 使用 URL 和文件名安全型 base64 編碼方案。
String s1 = Base64.getEncoder().encodeToString(strByte);
System.out.println("URL編碼: " + s1);
String ss1 = new String(Base64.getUrlDecoder().decode(s));
System.out.println("解碼: " + ss1);
// 使用 MIME 型 base64 編碼方案
String s2 = Base64.getMimeEncoder().encodeToString(strByte);
System.out.println("MIME編碼: " + s2);
String ss2 = new String(Base64.getMimeDecoder().decode(s));
System.out.println("解碼: " + ss2);
}
}
結(jié)果:
基本編碼: a2pneW0=
解碼: kjgym
URL編碼: a2pneW0=
解碼: kjgym
MIME編碼: a2pneW0=
解碼: kjgym
閱讀源碼
- 先來總體看一下
Base
類王财,該類主要包含兩個(gè)內(nèi)嵌類Encoder
和Decoder
以及七個(gè)構(gòu)造內(nèi)嵌類的方法:
-
再看兩個(gè)內(nèi)嵌類卵迂。比如
Encoder
(部分源代碼):public static class Encoder { private final byte[] newline; private final int linemax; private final boolean isURL; private final boolean doPadding; private Encoder(boolean isURL, byte[] newline, int linemax, boolean doPadding { this.isURL = isURL; this.newline = newline; this.linemax = linemax; this.doPadding = doPadding; } static final Encoder RFC4648 = new Encoder(false, null, -1, true); static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1, true); static final Encoder RFC2045 = new Encoder(false, CRLF, MIMELINEMAX, true); public byte[] encode(byte[] src) {} public int encode(byte[] src, byte[] dst) {} @SuppressWarnings("deprecation") public String encodeToString(byte[] src) {} public ByteBuffer encode(ByteBuffer buffer) {} public OutputStream wrap(OutputStream os) {} public Encoder withoutPadding() {} private int encode0(byte[] src, int off, int end, byte[] dst) {} }
構(gòu)造器私有化,對(duì)外提供單個(gè)靜態(tài)對(duì)象绒净,然后在Base 64類中见咒,使用
getEncoder
的方式愛調(diào)用,使用了單例模式挂疆。static final Encoder RFC4648 = new Encoder(false, null, -1, true); static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1, true); static final Encoder RFC2045 = new Encoder(false, CRLF, MIMELINEMAX, true);
Decoder
與上文Encoder
相似改览,這里不再贅述。有興趣自己Ctrl + 單擊
去看源碼缤言。
拓展
-
StandardCharsets
類宝当。在java.nio.charset
包下的一個(gè)類,在字符串和byte數(shù)組相互轉(zhuǎn)換需要制定編碼的時(shí)候使用胆萧。該類有六種編碼格式:/* * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package java.nio.charset; /** * Constant definitions for the standard {@link Charset Charsets}. These * charsets are guaranteed to be available on every implementation of the Java * platform. * * @see <a href="Charset#standard">Standard Charsets</a> * @since 1.7 */ public final class StandardCharsets { private StandardCharsets() { throw new AssertionError("No java.nio.charset.StandardCharsets instances for you!"); } /** * Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the * Unicode character set */ public static final Charset US_ASCII = Charset.forName("US-ASCII"); /** * ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1 */ public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); /** * Eight-bit UCS Transformation Format */ public static final Charset UTF_8 = Charset.forName("UTF-8"); /** * Sixteen-bit UCS Transformation Format, big-endian byte order */ public static final Charset UTF_16BE = Charset.forName("UTF-16BE"); /** * Sixteen-bit UCS Transformation Format, little-endian byte order */ public static final Charset UTF_16LE = Charset.forName("UTF-16LE"); /** * Sixteen-bit UCS Transformation Format, byte order identified by an * optional byte-order mark */ public static final Charset UTF_16 = Charset.forName("UTF-16"); }