Base64 將任意二進位資料編碼為 64 個可列印 ASCII 字元 (A-Z, a-z, 0-9, +, /),解決郵件傳輸 (MIME)、XML/JSON 嵌入二進位、URL 參數傳遞、資料庫儲存 Blob 等場景的「非文本安全」問題。每 3 位元組 (24 bits) 轉為 4 個 Base64 字元 (4×6=24 bits),編碼後體積膨脹約 33%。
輸入: "men" (3 bytes = 24 bits) m(109) e(101) n(110) 二進位: 01101101 01100101 01101110 分組: 011011 010110 010101 101110 (4組 × 6 bits) 十進位: 27 22 21 46 Base64: b W V u 結果: "bWVu" 不足 3 bytes 時以 '=' 補齊至 4 字元: "me" → "bWU=" "m" → "bQ=="
public class MIMEBase64 {
private static final char[] BASE64_TABLE =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
public static void encode(String filename, BufferedWriter out) throws IOException {
byte[] bytes = Files.readAllBytes(Paths.get(filename));
int n = bytes.length;
if (n == 0) return;
int fullGroups = n / 3;
int remainder = n % 3;
int lineLength = 0;
// 處理完整 3-byte 組
for (int i = 0; i < fullGroups * 3; i += 3) {
int b1 = bytes[i] & 0xFF;
int b2 = bytes[i+1] & 0xFF;
int b3 = bytes[i+2] & 0xFF;
out.write(BASE64_TABLE[b1 >>> 2]);
out.write(BASE64_TABLE[(b1 & 0x03) << 4 | (b2 >>> 4)]);
out.write(BASE64_TABLE[(b2 & 0x0F) << 2 | (b3 >>> 6)]);
out.write(BASE64_TABLE[b3 & 0x3F]);
if (++lineLength >= 19) { // 19×4=76 字元
out.write('\n');
lineLength = 0;
}
}
// 處理尾部剩餘 1 或 2 bytes
if (remainder == 2) {
int b1 = bytes[n-2] & 0xFF;
int b2 = bytes[n-1] & 0xFF;
out.write(BASE64_TABLE[b1 >>> 2]);
out.write(BASE64_TABLE[(b1 & 0x03) << 4 | (b2 >>> 4)]);
out.write(BASE64_TABLE[(b2 & 0x0F) << 2]);
out.write('=');
} else if (remainder == 1) {
int b1 = bytes[n-1] & 0xFF;
out.write(BASE64_TABLE[b1 >>> 2]);
out.write(BASE64_TABLE[(b1 & 0x03) << 4]);
out.write('=');
out.write('=');
}
if (lineLength > 0) out.write('\n');
}
}
// 編碼
String encoded = Base64.getEncoder().encodeToString(bytes);
String encodedUrlSafe = Base64.getUrlEncoder().encodeToString(bytes); // URL 安全版 (-_ 替代 +/)
// 解碼
byte[] decoded = Base64.getDecoder().decode(encodedString);
byte[] decodedUrlSafe = Base64.getUrlDecoder().decode(encodedString);
// MIME 格式 (自動換行 76 字元)
String mimeEncoded = Base64.getMimeEncoder().encodeToString(bytes);
// 編碼輸出流
try (OutputStream os = Base64.getEncoder().wrap(outputStream)) {
os.write(data);
}
// 解碼輸入流
try (InputStream is = Base64.getDecoder().wrap(inputStream)) {
byte[] buffer = new byte[8192];
int len;
while ((len = is.read(buffer)) != -1) { /* 處理 */ }
}
| 坑點 | 解決方案 |
|---|---|
| 字串獲取位元組未指定編碼 | 必須指定 str.getBytes(StandardCharsets.UTF_8) |
| URL 參數含 + / = 導致解析錯誤 | 使用 Base64.getUrlEncoder() (輸出 - _ 無補齊) |
| 大檔案載入記憶體 OOM | 使用流式 wrap() 邊讀邊碼 |
| 解碼拋出 IllegalArgumentException | 輸入含非法字元或長度非 4 倍數,預處理過濾/補齊 |
| Android 與 JDK 行為差異 | Android 早期版本用 android.util.Base64,API 26+ 對齊 JDK |
Base64 是二進位資料進入文本世界的通用橋樑。理解其 3→4 位元組分組、位移遮罩、補齊機制是基本功,但生產代碼必須使用 JDK 內建 java.util.Base64 (JDK 8+)。針對 URL、MIME、大檔案等場景選擇對應 Encoder/Decoder,善用流式 API 避免記憶體壓力。舊版本專案可引入 Apache Commons Codec 或升級 JDK。