Base64 編解碼過程中可能遇到:輸入為 null、非法字符、填充錯誤、流讀取異常等情況。標準庫異常信息往往不夠具體,難以定位問題字符位置。本工具類提供增強異常信息,包含錯誤字符、位置、上下文,便於快速排查。
package com.gootrip.util;
import java.io.IOException;
public class Base64DecodingException extends IOException {
private final char invalidChar;
private final int position;
private final String context;
public Base64DecodingException(String message, char invalidChar, int position, String context) {
super(message);
this.invalidChar = invalidChar;
this.position = position;
this.context = context;
}
public Base64DecodingException(String message, char invalidChar) {
this(message, invalidChar, -1, null);
}
public char getInvalidChar() { return invalidChar; }
public int getPosition() { return position; }
public String getContext() { return context; }
@Override
public String getMessage() {
StringBuilder sb = new StringBuilder(super.getMessage());
if (invalidChar != 0) sb.append(", 非法字符: '").append(invalidChar).append("' (0x").append(Integer.toHexString(invalidChar)).append(")");
if (position >= 0) sb.append(", 位置: ").append(position);
if (context != null) sb.append(", 上下文: ").append(context);
return sb.toString();
}
}
package com.gootrip.util;
import java.io.*;
public class Base64Codec {
private static final char[] BASE64_CHARS =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
private static final byte[] DECODE_TABLE = new byte[256];
static {
for (int i = 0; i < 256; i++) DECODE_TABLE[i] = -1;
for (int i = 0; i < BASE64_CHARS.length; i++) {
DECODE_TABLE[BASE64_CHARS[i]] = (byte) i;
}
}
/**
* 安全編碼:處理 null、空數組
*/
public static String encode(byte[] data) {
if (data == null || data.length == 0) return "";
return java.util.Base64.getEncoder().encodeToString(data);
}
/**
* 安全解碼:詳細異常信息
*/
public static byte[] decode(String base64) throws Base64DecodingException {
if (base64 == null || base64.isEmpty()) return new byte[0];
// 移除空白字符
String clean = base64.replaceAll("\\s", "");
// 校驗長度
if (clean.length() % 4 != 0) {
throw new Base64DecodingException("Base64 長度必須是 4 的倍數", 0, clean.length(), clean.substring(0, Math.min(20, clean.length())));
}
// 校驗字符合法性
for (int i = 0; i < clean.length(); i++) {
char c = clean.charAt(i);
if (c != '=' && DECODE_TABLE[c] == -1) {
String ctx = clean.substring(Math.max(0, i-10), Math.min(clean.length(), i+10));
throw new Base64DecodingException("發現非法 Base64 字符", c, i, ctx);
}
}
try {
return java.util.Base64.getDecoder().decode(clean);
} catch (IllegalArgumentException e) {
throw new Base64DecodingException("JDK 解碼失敗: " + e.getMessage(), 0, -1, clean.substring(0, Math.min(50, clean.length())));
}
}
/**
* 流式解碼:大數據場景
*/
public static void decodeStream(InputStream in, OutputStream out) throws IOException, Base64DecodingException {
try (InputStream b64in = java.util.Base64.getDecoder().wrap(in)) {
byte[] buffer = new byte[8192];
int len;
while ((len = b64in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
} catch (IOException e) {
throw new Base64DecodingException("流解碼失敗: " + e.getMessage(), 0);
}
}
}
public class Base64CodecTest {
public static void main(String[] args) {
// 正常編解碼
String original = "Hello, 世界! 🚀";
String encoded = Base64Codec.encode(original.getBytes(StandardCharsets.UTF_8));
System.out.println("編碼: " + encoded);
try {
byte[] decoded = Base64Codec.decode(encoded);
System.out.println("解碼: " + new String(decoded, StandardCharsets.UTF_8));
} catch (Base64DecodingException e) {
System.err.println("解碼異常: " + e.getMessage());
e.printStackTrace();
}
// 非法字符測試
try {
Base64Codec.decode("SGVsbG8gV29ybGQhIQ==="); // 多一個 =
} catch (Base64DecodingException e) {
System.err.println("預期異常: " + e.getMessage());
}
try {
Base64Codec.decode("SGVsbG8gV29ybGQh!"); // 非法字符 !
} catch (Base64DecodingException e) {
System.err.println("預期異常: " + e.getMessage());
// 輸出: 非法字符: '!' (0x21), 位置: 16, 上下文: ...ldh!
}
}
}
java.util.Base64 保證性能與標準兼容| 場景 | JDK IllegalArgumentException | Base64DecodingException |
|---|---|---|
| 非法字符 | Illegal base64 character | 非法字符: '!' (0x21), 位置: 16, 上下文: ... |
| 填充錯誤 | Invalid padding | Base64 長度必須是 4 的倍數, 位置: 20... |
| 流讀取錯誤 | IOException | 流解碼失敗: ... (保留原始異常鏈) |
Base64.getUrlEncoder())通過自定義異常類攜帶豐富上下文信息,配合預校驗與委託 JDK 核心實現,構建出既健壯又高性能的 Base64 編解碼工具類。此設計模式可推廣到其他編碼/解析場景(JSON、XML、Protobuf 等),顯著提升生產環境問題排查效率。