1. 概述

本文深入探讨 Java 中实现 Base64 编码与解码的多种方式。主要内容包括:

✅ Java 8 原生提供的 java.util.Base64 API
✅ Apache Commons Codec 库的使用
✅ 常见踩坑点与最佳实践

Base64 并非加密手段,而是一种二进制数据转文本字符串的编码方式,广泛用于 HTTP 接口传输、JWT Token、图片内联等场景。本文面向有一定经验的开发者,基础概念不赘述。


2. Java 8 原生 Base64 支持

Java 8 终于在标准库中引入了 java.util.Base64 工具类,无需再依赖第三方库,简单粗暴又安全。

该类提供了三种编码器:

  • Basic:基础编码,无换行
  • URL and Filename Safe:适用于 URL 和文件名
  • MIME:符合 MIME 标准,每行最多 76 字符

2.1 基础 Base64 编码/解码

基础编码使用 A-Za-z0-9+/ 字符集,输出无换行。

String originalInput = "test input";
String encodedString = Base64.getEncoder().encodeToString(originalInput.getBytes());

⚠️ 注意:getBytes() 默认使用平台编码(如 Windows 是 GBK),建议显式指定字符集避免乱码。

解码也很直观:

byte[] decodedBytes = Base64.getDecoder().decode(encodedString);
String decodedString = new String(decodedBytes, StandardCharsets.UTF_8);

✅ 推荐始终指定 UTF-8 字符集,避免跨平台问题。

2.2 无填充(Without Padding)编码

Base64 要求输出长度为 4 的倍数,不足时用 = 填充。但在某些场景(如生成 token)中,= 可能引发 URL 转义问题。

此时可使用无填充编码:

String encodedString = 
    Base64.getEncoder().withoutPadding().encodeToString(originalInput.getBytes(StandardCharsets.UTF_8));

⚠️ 无填充编码后,必须使用对应的无填充解码器,否则会抛 IllegalArgumentException

// ❌ 错误:使用普通解码器
Base64.getDecoder().decode(encodedString); // 可能报错

// ✅ 正确:使用无填充解码器
Base64.getDecoder().decode(encodedString); // 实际上 Java 8 的 getDecoder() 能自动处理无填充

📌 小知识:Java 8 的 getDecoder() 其实能兼容无填充字符串,但为了语义清晰,建议编码时用 withoutPadding(),解码时无需特别处理。

2.3 URL 安全 Base64 编码

当 Base64 字符串用于 URL 参数或文件名时,+/ 可能引起问题(如被误认为路径分隔符)。此时应使用 URL 安全编码,它将 + 替换为 -/ 替换为 _

String originalUrl = "https://www.example.com/?q=java+base64";
String encodedUrl = Base64.getUrlEncoder().encodeToString(originalUrl.getBytes(StandardCharsets.UTF_8));

解码使用对应的 URL 解码器:

byte[] decodedBytes = Base64.getUrlDecoder().decode(encodedUrl);
String decodedUrl = new String(decodedBytes, StandardCharsets.UTF_8);

✅ 这种编码方式在 JWT、OAuth Token 中非常常见,属于“标准操作”。

2.4 MIME Base64 编码

MIME 编码用于邮件等场景,每行不超过 76 个字符,并以 \r\n 换行。

先准备一段长文本:

private static StringBuilder getMimeBuffer() {
    StringBuilder buffer = new StringBuilder();
    for (int count = 0; count < 10; ++count) {
        buffer.append(java.util.UUID.randomUUID().toString());
    }
    return buffer;
}

进行 MIME 编码:

StringBuilder buffer = getMimeBuffer();
String encodedMime = Base64.getMimeEncoder().encodeToString(buffer.toString().getBytes(StandardCharsets.UTF_8));

输出示例(每行 76 字符):

OWQzZDQ1YjMtZjIzYi00Y2I0LWJhZjktY2U1ZjU3ZjM5YjQ1CjE0NzUyZmU5LTg4NDMtNGUw
YS1hZjY2LWU1Y2MwNzQ5M2EwNQphYjMwZjQwZi03ZjU5LTRlZjktYjZjMi1jZjFhZDQxZDc1
...

解码:

byte[] decodedBytes = Base64.getMimeDecoder().decode(encodedMime);
String decodedMime = new String(decodedBytes, StandardCharsets.UTF_8);

⚠️ MIME 解码器能自动忽略换行符,无需预处理。


3. 使用 Apache Commons Codec

虽然 Java 8 提供了原生支持,但 Commons Codec 依然广泛用于老项目或需要更灵活控制的场景。

3.1 添加依赖

<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.16.0</version>
</dependency>

3.2 创建 Base64 实例

Commons Codec 提供多个构造函数:

  • new Base64():基础编码
  • new Base64(true):URL 安全模式
  • new Base64(76, "\r\n".getBytes()):自定义行长度和分隔符

示例:

String originalInput = "test input";
Base64 base64 = new Base64();
byte[] encodedBytes = base64.encode(originalInput.getBytes(StandardCharsets.UTF_8));
String encodedString = new String(encodedBytes);

解码:

byte[] decodedBytes = base64.decode(encodedString.getBytes(StandardCharsets.UTF_8));
String decodedString = new String(decodedBytes, StandardCharsets.UTF_8);

3.3 静态方法(推荐)

更简洁的方式是使用静态工具方法,无需创建实例:

String originalInput = "test input";

// 编码
String encodedString = new String(Base64.encodeBase64(originalInput.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);

// 解码
byte[] decodedBytes = Base64.decodeBase64(encodedString.getBytes(StandardCharsets.UTF_8));
String decodedString = new String(decodedBytes, StandardCharsets.UTF_8);

✅ 静态方法线程安全,性能更好,推荐日常使用。


4. String 与 byte[] 转换技巧

Base64 处理的是字节数组,因此 String 与 byte[] 的转换是关键。

4.1 普通字符串转 byte[]

String originalInput = "test input";
byte[] result = originalInput.getBytes(StandardCharsets.UTF_8); // ✅ 推荐

⚠️ 避免使用无参 getBytes(),它依赖系统默认编码,可能引发乱码。

4.2 Base64 字符串解码为 byte[]

String base64String = "dGVzdCBpbnB1dA==";
byte[] result = Base64.getDecoder().decode(base64String);
String decoded = new String(result, StandardCharsets.UTF_8);

4.3 使用 JAXB 的 DatatypeConverter(已弃用)

⚠️ 注意:javax.xml.bind.DatatypeConverter 在 Java 9+ 中已标记为弃用,不推荐新项目使用。

// ❌ 不推荐,仅用于兼容老代码
String base64String = "dGVzdCBpbnB1dA==";
byte[] result = DatatypeConverter.parseBase64Binary(base64String);

// 十六进制转 byte[]
String hexString = "7465737420696E707574";
byte[] hexBytes = DatatypeConverter.parseHexBinary(hexString);

✅ 新项目建议使用 java.util.HexFormat(Java 17+)或 Commons Codec 的 Hex 类。


5. 总结

方式 适用场景 推荐指数
java.util.Base64 Java 8+ 项目,标准需求 ✅✅✅✅✅
Base64.getUrlEncoder() URL/Token 场景 ✅✅✅✅✅
Base64.getMimeEncoder() 邮件、大文本 ✅✅✅✅
Apache Commons Codec 老项目、需自定义控制 ✅✅✅✅
DatatypeConverter 老系统兼容 ⚠️ 不推荐

📌 核心建议:

  • ✅ Java 8+ 优先使用原生 Base64
  • ✅ 显式指定字符集(UTF-8
  • ✅ URL 场景使用 getUrlEncoder()
  • ✅ 避免使用已弃用的 DatatypeConverter

所有示例代码已上传至 GitHub:https://github.com/example/java-base64-demo


原始标题:Java Base64 Encoding and Decoding