Base64 編碼將二進制圖片轉為文本字符串,廣泛用於前端內嵌圖片(Data URI)、API 傳輸、數據庫存儲等場景。本文提供基於 JDK 原生 java.util.Base64(Java 8+)與舊版 sun.misc.BASE64Encoder/Decoder 的完整互轉方案,並封裝為生產級工具類。
| 版本 | 編碼類 | 解碼類 | 備註 |
|---|---|---|---|
| Java 8+ (推薦) | java.util.Base64.Encoder | java.util.Base64.Decoder | 標準 API、無依賴、支持 URL/MIME 變體 |
| Java 8 前 | sun.misc.BASE64Encoder | sun.misc.BASE64Decoder | 內部 API、不保證跨 JDK 兼容、編碼輸出含換行符 |
import java.io.*;
import java.util.Base64;
public class Base64ImageUtils {
/** 圖片文件 → Base64 字符串 */
public static String encodeToString(String imagePath) throws IOException {
byte[] bytes = Files.readAllBytes(Paths.get(imagePath));
return Base64.getEncoder().encodeToString(bytes);
}
/** 圖片文件 → Data URI (前端直接用於 <img src="...">) */
public static String encodeToDataUri(String imagePath, String mimeType) throws IOException {
return "data:" + mimeType + ";base64," + encodeToString(imagePath);
}
/** Base64 字符串 → 圖片文件 */
public static void decodeToFile(String base64, String outputPath) throws IOException {
// 兼容 Data URI 前綴
if (base64.contains(",")) base64 = base64.split(",")[1];
byte[] bytes = Base64.getDecoder().decode(base64);
Files.write(Paths.get(outputPath), bytes);
}
/** InputStream → Base64 (適用於上傳流直轉) */
public static String encodeStream(InputStream in) throws IOException {
return Base64.getEncoder().encodeToString(in.readAllBytes());
}
}
import sun.misc.BASE64Encoder;
import sun.misc.BASE64Decoder;
public class LegacyBase64Image {
public static String getImageStr(String imgFilePath) {
try (InputStream in = new FileInputStream(imgFilePath)) {
byte[] data = in.readAllBytes(); // Java 9+; Java 8 用 ByteArrayOutputStream 手動讀取
return new BASE64Encoder().encode(data);
} catch (IOException e) { throw new RuntimeException(e); }
}
public static boolean generateImage(String imgStr, String imgFilePath) {
if (imgStr == null) return false;
try {
byte[] bytes = new BASE64Decoder().decodeBuffer(imgStr);
// 修正負字節(舊版解碼可能產生負值)
for (int i = 0; i < bytes.length; i++) if (bytes[i] < 0) bytes[i] += 256;
try (OutputStream out = new FileOutputStream(imgFilePath)) {
out.write(bytes);
}
return true;
} catch (Exception e) { return false; }
}
}
@GetMapping("/avatar/{userId}")
public String avatarAsDataUri(@PathVariable Long userId) throws IOException {
String path = userService.getAvatarPath(userId);
String mime = Files.probeContentType(Paths.get(path)); // image/png, image/jpeg...
return Base64ImageUtils.encodeToDataUri(path, mime);
}
// 前端: <img src="${avatarUrl}" />
@PostMapping("/upload")
public R upload(@RequestBody Map<String, String> payload) {
String base64 = payload.get("image"); // 前端 canvas.toDataURL() 獲得
String fileName = UUID.randomUUID() + ".png";
Base64ImageUtils.decodeToFile(base64, "/data/uploads/" + fileName);
return R.ok("https://cdn.example.com/" + fileName);
}
將 Base64 字符串存入 TEXT/CLOB 字段,避免 BLOB 流式讀寫複雜性,適合中小圖片(<1MB)。
| 坑點 | 現象 | 解決 |
|---|---|---|
| Data URI 前綴干擾解碼 | 解碼報錯/圖片損壞 | 解碼前 split(",")[1] 移除前綴 |
| 大文件 OOM | Heap 爆滿 | 分塊流式編碼/解碼,勿一次性 readAllBytes |
| 舊版解碼產生負字節 | 圖片顏色錯亂 | 遍歷字節數組 if(b<0) b+=256 |
| 編碼輸出含換行符 | 前端解析失敗 | 用 Base64.getEncoder().withoutPadding() 或手動 replaceAll("\\s","") |
| MIME 類型不匹配 | 瀏覽器不顯示 | Data URI 必須聲明正確 mimeType |
Base64.Encoder/Decoder 線程安全,建議聲明為 static final 復用。Base64.getEncoder().wrap(outputStream) 邊讀邊寫,避免全量載入內存。Java 8+ 專案強烈建議遷移至 java.util.Base64,代碼更簡潔、標準、無兼容性風險。封裝通用工具類時,請統一處理 Data URI 前綴、異常轉換、大文件流式化,讓業務代碼保持極簡。