Java Base64 解码 — getDecoder()

·Backend Engineer·审阅者Aisha Osei·发布日期

直接在浏览器中使用免费的 Base64解码器,无需安装。

在线试用 Base64解码器 →

在 Java 开发中,Base64 解码是我每隔几天就会用到的操作——从 Kubernetes 环境变量中提取密钥、 读取 REST API 返回的二进制数据、在调试时检查 JWT 令牌。Java 内置的 java.util.Base64 类(自 JDK 8 起提供)提供三种解码器: getDecoder() 用于标准 Base64,getUrlDecoder() 用于 URL 安全输入,getMimeDecoder() 用于邮件附件等换行数据。如果只需快速验证而不想写代码, ToolDeck 的 Base64 解码器 可以直接在浏览器中完成。本文面向 Java 8+ 用户,涵盖三种解码器、使用 wrap(InputStream) 进行流式处理、提取 JWT 载荷、解码文件和 API 响应、 Apache Commons Codec 替代方案,以及四种在生产中导致乱码输出的常见错误。

  • Base64.getDecoder().decode(s) 是标准方式——自 JDK 8 起内置于 java.util.Base64,无需任何依赖。
  • 处理 JWT 令牌和 OAuth 载荷时使用 getUrlDecoder()——它们使用 - 和 _ 字符集,而非 + 和 /。
  • getMimeDecoder() 会忽略换行符和空白字符,是处理邮件附件和 PEM 证书的正确选择。
  • decoder.wrap(InputStream) 支持大文件的动态解码,无需将全部内容加载到内存中。
  • 基本解码器是严格的——尾部换行符、空格或错误的字符集字符会立即抛出 IllegalArgumentException。

什么是 Base64 解码?

Base64 编码将二进制数据转换为 64 个 ASCII 字符的表示形式,以便在纯文本通道中安全传输—— 包括 JSON 字段、HTTP 头、XML 文档、邮件正文。解码是其逆过程:每 4 个 Base64 字符还原为 3 个原始字节。末尾的 = 填充字符表示最后一组填充了多少字节。Base64 不是加密—— 任何人都可以还原它。其目的是保证传输安全,而非保密。

Java 中典型的解码场景:提取以 Base64 形式注入的配置值,解包云 API 响应中的二进制文件内容, 读取 PEM 编码的证书,以及在调试时检查 JWT 令牌载荷。

Before · text
After · text
ZGItcHJvZC51cy1lYXN0LTEuYW1hem9uYXdzLmNvbTo1NDMy
db-prod.us-east-1.amazonaws.com:5432

Base64.getDecoder().decode() — 标准解码方法

java.util.Base64 类在 JDK 8 中引入,取代了过去大家依赖的 sun.misc.BASE64Decoder。无需外部依赖——只需 import java.util.Base64 并调用 Base64.getDecoder().decode()。该方法接受 String byte[],并返回包含解码数据的 byte[]

最简可运行示例

Java 8+
import java.util.Base64;
import java.nio.charset.StandardCharsets;

public class DecodeCredential {
    public static void main(String[] args) {
        // Kubernetes secret value, Base64-encoded
        String encoded = "ZGItcHJvZC51cy1lYXN0LTEuYW1hem9uYXdzLmNvbTo1NDMy";

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

        System.out.println(connectionString);
        // db-prod.us-east-1.amazonaws.com:5432
    }
}

构造 String 时务必指定 StandardCharsets.UTF_8。 无参的 new String(bytes) 构造函数使用平台默认编码, 不同系统上的结果可能不同。在以 Cp1252 为默认编码的 Windows 服务器上, 多字节 UTF-8 字符会被悄无声息地损坏。

往返验证

Java 8+
import java.util.Base64;
import java.nio.charset.StandardCharsets;

public class RoundTrip {
    public static void main(String[] args) {
        String original = "redis://cache-prod.internal:6379/session-store";

        String encoded = Base64.getEncoder().encodeToString(
            original.getBytes(StandardCharsets.UTF_8)
        );
        System.out.println(encoded);
        // cmVkaXM6Ly9jYWNoZS1wcm9kLmludGVybmFsOjYzNzkvc2Vzc2lvbi1zdG9yZQ==

        byte[] decoded = Base64.getDecoder().decode(encoded);
        String recovered = new String(decoded, StandardCharsets.UTF_8);

        System.out.println(recovered.equals(original)); // true
    }
}

解码到预分配缓冲区

三参数重载 decode(byte[] src, byte[] dst) 直接写入目标缓冲区, 并返回写入的字节数。这可以在热路径上避免额外的内存分配:

Java 8+
import java.util.Base64;

public class DecodeToBuffer {
    public static void main(String[] args) {
        byte[] src = "eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0M30=".getBytes();
        byte[] dst = new byte[1024]; // pre-allocated

        int len = Base64.getDecoder().decode(src, dst);
        String result = new String(dst, 0, len);

        System.out.println(result);
        // {"host":"10.0.1.50","port":8443}
    }
}
注意:decode() 在输入包含 Base64 字符集之外的字符(包括换行符和空格)时, 会抛出 IllegalArgumentException。如果输入可能含有空白字符, 请换用 getMimeDecoder(),或在解码前调用 encoded.strip() 去除空白。

解码 Base64 到非标准类型和自定义对象

decode() 返回的原始 byte[] 通常需要转换为更具体的类型: UUID、序列化的 Java 对象、protobuf 消息或时间戳。解码器本身始终返回字节—— 将其转换为领域类型是开发者的责任。

Base64 转 UUID

Java 8+
import java.util.Base64;
import java.nio.ByteBuffer;
import java.util.UUID;

public class DecodeUUID {
    public static UUID fromBase64(String encoded) {
        byte[] bytes = Base64.getUrlDecoder().decode(encoded);
        ByteBuffer bb = ByteBuffer.wrap(bytes);
        return new UUID(bb.getLong(), bb.getLong());
    }

    public static void main(String[] args) {
        // Compact Base64-encoded UUID from an API response
        String encoded = "f47ac10b-58cc-4372-a567-0e02b2c3d479";
        UUID original = UUID.fromString(encoded);

        // Encode to Base64 (compact form — 22 chars vs 36)
        ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
        bb.putLong(original.getMostSignificantBits());
        bb.putLong(original.getLeastSignificantBits());
        String compact = Base64.getUrlEncoder().withoutPadding()
            .encodeToString(bb.array());
        System.out.println(compact); // 9HrBC1jMQ3KlZw4CssPUeQ

        // Decode back
        UUID recovered = fromBase64(compact);
        System.out.println(recovered); // f47ac10b-58cc-4372-a567-0e02b2c3d479
    }
}

Base64 转 Jackson 反序列化 JSON 对象

Java 8+
import java.util.Base64;
import java.nio.charset.StandardCharsets;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.Instant;

public class DecodeJsonPayload {
    record DeployEvent(String service, String region, Instant deployedAt, int replicas) {}

    public static void main(String[] args) throws Exception {
        // Base64-encoded JSON payload from a message queue
        String encoded = "eyJzZXJ2aWNlIjoicGF5bWVudC1nYXRld2F5Iiwi"
            + "cmVnaW9uIjoiZXUtd2VzdC0xIiwiZGVwbG95ZWRBdCI6"
            + "IjIwMjYtMDMtMTVUMTQ6MzA6MDBaIiwicmVwbGljYXMiOjR9";

        byte[] jsonBytes = Base64.getDecoder().decode(encoded);
        String json = new String(jsonBytes, StandardCharsets.UTF_8);
        System.out.println(json);
        // {"service":"payment-gateway","region":"eu-west-1",
        //  "deployedAt":"2026-03-15T14:30:00Z","replicas":4}

        ObjectMapper mapper = new ObjectMapper();
        mapper.findAndRegisterModules(); // picks up JavaTimeModule
        DeployEvent event = mapper.readValue(jsonBytes, DeployEvent.class);

        System.out.println(event.service());   // payment-gateway
        System.out.println(event.deployedAt()); // 2026-03-15T14:30:00Z
    }
}
警告:永远不要使用 ObjectInputStream 反序列化不可信的 Base64 数据。Java 反序列化攻击已有大量文档记录——如果编码内容来自外部来源,请将其解析为 JSON 或 protobuf, 而非使用 Java 原生序列化。

Base64.Decoder 方法参考

所有方法均属于 java.util.Base64 及其内部类 Base64.DecoderBase64 上的三个工厂方法返回不同的解码器实例;decode() wrap() 方法位于 Decoder 实例上。

方法
返回值
输入类型
说明
getDecoder()
Base64.Decoder
标准解码器(RFC 4648 §4,使用 + 和 / 字符集,带 = 填充)
getUrlDecoder()
Base64.Decoder
URL 安全解码器(RFC 4648 §5,使用 - 和 _ 字符集,带 = 填充)
getMimeDecoder()
Base64.Decoder
MIME 解码器——忽略换行符及非 Base64 字符
decode(String src)
byte[]
String
将输入字符串解码为新的字节数组
decode(byte[] src)
byte[]
byte[]
将输入字节数组解码为新的字节数组
decode(byte[] src, byte[] dst)
int
byte[] + byte[]
解码到预分配的 dst 缓冲区,返回写入的字节数
wrap(InputStream is)
InputStream
InputStream
返回一个在读取时动态解码 Base64 数据的流

getMimeDecoder() — 解码换行及 MIME Base64

基本解码器会拒绝 Base64 字符集之外的任何字符——包括 MIME 编码内容中必然存在的 \r\n 换行符。邮件附件、PEM 证书以及一些较老的 API 响应会在每行 76 个字符处换行。getMimeDecoder() 会静默忽略换行符及任何不在 Base64 字符集中的字符,因此可以开箱即用地处理这类数据。

Java 8+
import java.util.Base64;
import java.nio.charset.StandardCharsets;

public class MimeDecode {
    public static void main(String[] args) {
        // PEM certificate body — line-wrapped at 76 characters
        String pemBody = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t\r\n"
            + "TUlJQm96Q0NBVWlnQXdJQkFnSUpBSXBhVDJU\r\n"
            + "aVFvZU1BMEdDU3FHU0liM0RRRU==";

        // getDecoder() would throw IllegalArgumentException here
        byte[] decoded = Base64.getMimeDecoder().decode(pemBody);
        System.out.println(new String(decoded, StandardCharsets.UTF_8));
        // -----BEGIN CERTIFICATE-----
        // MIIBozCCAUigAwIBAgIJAIpaT2T...
    }
}
注意:getMimeDecoder() 是宽松模式:它会跳过无效字符而非抛出异常。 对于已知的 MIME 数据来说没有问题,但对于任意输入,它可能会悄无声息地忽略数据损坏。 当需要严格验证时,请使用 getDecoder()

从文件和 API 响应中解码 Base64

从磁盘读取 Base64 编码的文件

二进制文件(图像、证书、加密 blob)有时以 Base64 文本形式存储在磁盘上。 读取文件、解码、写入二进制输出:

Java 8+
import java.util.Base64;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public class DecodeFile {
    public static void main(String[] args) {
        Path inputPath = Path.of("tls-cert.pem.b64");
        Path outputPath = Path.of("tls-cert.pem");

        try {
            String encoded = Files.readString(inputPath).strip();
            byte[] decoded = Base64.getMimeDecoder().decode(encoded);
            Files.write(outputPath, decoded);

            System.out.printf("Decoded %d bytes → %s%n", decoded.length, outputPath);
        } catch (IOException e) {
            System.err.println("File error: " + e.getMessage());
        } catch (IllegalArgumentException e) {
            System.err.println("Invalid Base64: " + e.getMessage());
        }
    }
}

解码 HTTP API 响应中的 Base64 字段

云 API(AWS KMS、GitHub Contents、Vault)经常在 JSON 中以 Base64 字符串形式返回二进制数据。 先解析 JSON,再解码目标字段:

Java 11+
import java.util.Base64;
import java.nio.charset.StandardCharsets;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class DecodeApiResponse {
    public static void main(String[] args) {
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.example.com/secrets/db-password"))
            .header("Authorization", "Bearer sk-prod-9f8e7d6c")
            .build();

        try {
            HttpResponse<String> response = client.send(
                request, HttpResponse.BodyHandlers.ofString());

            if (response.statusCode() != 200) {
                System.err.printf("Unexpected status: %d%n", response.statusCode());
                return;
            }

            ObjectMapper mapper = new ObjectMapper();
            JsonNode root = mapper.readTree(response.body());

            // API returns: {"name":"db-password","value":"cG9zdGdyZXM6eGs5...","version":3}
            String encodedValue = root.get("value").asText();
            byte[] decoded = Base64.getDecoder().decode(encodedValue);
            String secret = new String(decoded, StandardCharsets.UTF_8);

            System.out.println("Secret: " + secret);
            // Secret: postgres:xk9mP2qR@db-prod:5432/orders
        } catch (Exception e) {
            System.err.println("Failed to fetch secret: " + e.getMessage());
        }
    }
}
注意:将解码调用单独放在捕获 IllegalArgumentException 的 try-catch 中, 与网络错误的异常处理分开。将 I/O 异常和解码失败混在一起会增加调试难度—— 你需要立即判断是 API 返回了错误数据还是网络出现了故障。

从命令行进行 Base64 解码

并非每次都需要编写 Java 程序。每台 Linux 和 macOS 系统都有 base64 命令, JDK 9+ 还提供了 jshell 用于交互式 Java 单行命令。 在调试时,这些方式比编译一个类要快得多。

bash
# Decode a Base64 string (Linux / macOS)
echo "eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0M30=" | base64 --decode
# {"host":"10.0.1.50","port":8443}

# Decode and pretty-print with jq
echo "eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0M30=" | base64 --decode | jq .
# {
#   "host": "10.0.1.50",
#   "port": 8443
# }

# Quick decode with jshell (JDK 9+)
echo 'System.out.println(new String(java.util.Base64.getDecoder().decode("c2VydmVyLWNvbmZpZw==")))' | jshell -
# server-config

# macOS uses -D instead of --decode
echo "c2VydmVyLWNvbmZpZw==" | base64 -D

如果需要将编码字符串直接粘贴到浏览器中验证, ToolDeck 的 Base64 解码器 无需任何配置即可处理标准和 URL 安全两种变体。

高性能替代方案:Apache Commons Codec

Java 内置的 java.util.Base64 已经经过充分优化——JDK 11+ 在 x86 上对编解码 使用了 intrinsic 优化。对大多数应用而言,没有理由引入第三方库。话虽如此,Apache Commons Codec 在遗留代码库中仍然很流行,并提供 Base64InputStream,支持带自动空白字符 处理的流式解码。

XML (Maven)
<!-- pom.xml -->
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.17.0</version>
</dependency>
Java 8+
import org.apache.commons.codec.binary.Base64;

public class CommonsCodecDecode {
    public static void main(String[] args) {
        // Commons Codec is more lenient — handles whitespace and line breaks
        String encoded = "eyJob3N0IjoiMTAuMC4xLjUw\nIiwicG9ydCI6ODQ0M30=";
        byte[] decoded = Base64.decodeBase64(encoded);

        System.out.println(new String(decoded));
        // {"host":"10.0.1.50","port":8443}
    }
}

Commons Codec 相比内置 API 的主要优势在于默认对空白字符更宽松,以及其 Base64InputStream 类——该类早于 Java 的 decoder.wrap()。 如果你使用 Java 8+,内置 API 已经覆盖了 Commons Codec 的所有功能。 我只在项目已经依赖它的情况下才会使用 Commons Codec。

使用 decoder.wrap() 流式处理大型 Base64 文件

Files.readString() 加载一个 200 MB 的 Base64 文件再调用 decode(),大约需要分配 350 MB 的堆内存:编码字符串加上解码后的字节数组。decoder.wrap(InputStream) 在读取时动态解码,使内存占用保持恒定。

Java 8+
import java.util.Base64;
import java.io.*;
import java.nio.file.*;

public class StreamDecode {
    public static void main(String[] args) throws IOException {
        Path src = Path.of("database-dump.sql.b64");
        Path dst = Path.of("database-dump.sql");

        try (InputStream in = Base64.getMimeDecoder().wrap(
                 new BufferedInputStream(Files.newInputStream(src)));
             OutputStream out = new BufferedOutputStream(Files.newOutputStream(dst))) {

            byte[] buffer = new byte[8192];
            int bytesRead;
            long total = 0;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
                total += bytesRead;
            }
            System.out.printf("Decoded %d bytes → %s%n", total, dst);
        }
    }
}

在 Java 9+ 上,可以用 in.transferTo(out) 替换读取循环—— 效果相同但代码更简洁。如果文件可能包含换行符(PEM 文件、邮件导出), 请使用 getMimeDecoder().wrap() 而非 getDecoder().wrap()

Java 9+
import java.util.Base64;
import java.io.*;
import java.nio.file.*;

public class StreamDecodeSimple {
    public static void main(String[] args) throws IOException {
        try (InputStream in = Base64.getMimeDecoder().wrap(
                 new BufferedInputStream(Files.newInputStream(Path.of("backup.tar.b64"))));
             OutputStream out = Files.newOutputStream(Path.of("backup.tar"))) {
            in.transferTo(out); // Java 9+
        }
    }
}
警告:getDecoder().wrap() 能容忍流中的换行符。 如果 Base64 数据有换行符(每 76 个字符换行),请改用 getMimeDecoder().wrap()——否则流会在不可预知的读取位置产生损坏的输出或抛出异常。

在 Java 中无需 JWT 库解码 Base64 JWT 载荷

JWT 由三个 Base64url 编码的段通过点号连接而成。中间的段是载荷——也是调试时最关心的部分。 无需引入 jjwt 或 Nimbus 即可解码。以 . 分割,用 getUrlDecoder() 解码第二段,再解析得到的 JSON:

Java 8+
import java.util.Base64;
import java.nio.charset.StandardCharsets;

public class JWTInspect {
    public static String decodeJwtPayload(String token) {
        String[] parts = token.split("\\.");
        if (parts.length != 3) {
            throw new IllegalArgumentException(
                "Invalid JWT: expected 3 segments, got " + parts.length);
        }
        // JWT uses URL-safe Base64 without padding
        byte[] payload = Base64.getUrlDecoder().decode(parts[1]);
        return new String(payload, StandardCharsets.UTF_8);
    }

    public static void main(String[] args) {
        String token = "eyJhbGciOiJSUzI1NiJ9"
            + ".eyJzdWIiOiJ1c3ItNjcyIiwiaXNzIjoiYXV0aC5leGFtcGxlLmNvbSIs"
            + "ImV4cCI6MTc0MTk1NjgwMCwicm9sZXMiOlsiYWRtaW4iLCJiaWxsaW5nIl19"
            + ".SIGNATURE_PLACEHOLDER";

        String payload = decodeJwtPayload(token);
        System.out.println(payload);
        // {"sub":"usr-672","iss":"auth.example.com",
        //  "exp":1741956800,"roles":["admin","billing"]}
    }
}

常见错误

这些错误我在代码审查中都遇到过,前两个占据了 Java 服务中绝大多数 Base64 相关的生产 bug。

对 URL 安全输入使用 getDecoder()

问题: JWT 令牌和 OAuth 访问令牌使用 URL 安全字符集(- 和 _)。将它们传给 getDecoder() 会抛出 IllegalArgumentException,因为 - 不在标准 Base64 字符集中。

解决方案: 检查数据来源:来自认证系统的令牌需要 getUrlDecoder();MIME 附件需要 getMimeDecoder()。

Before · Java
After · Java
// JWT header — URL-safe, no padding
String header = "eyJhbGciOiJSUzI1NiJ9";
byte[] decoded = Base64.getDecoder().decode(header);
// IllegalArgumentException: Illegal base64 character 2d
String header = "eyJhbGciOiJSUzI1NiJ9";
byte[] decoded = Base64.getUrlDecoder().decode(header);
System.out.println(new String(decoded));
// {"alg":"RS256"}
构造 String 时未指定字符集

问题: new String(bytes) 使用 JVM 默认字符集,不同环境下结果不同。Linux CI 服务器(UTF-8)和 Windows 生产主机(Cp1252)对相同字节会产生不同的结果。

解决方案: 始终将 StandardCharsets.UTF_8 作为第二个参数传入。

Before · Java
After · Java
byte[] decoded = Base64.getDecoder().decode(encoded);
String result = new String(decoded);
// platform-dependent — may corrupt multi-byte characters
byte[] decoded = Base64.getDecoder().decode(encoded);
String result = new String(decoded, StandardCharsets.UTF_8);
// consistent across all platforms
解码含尾部空白字符的字符串

问题: 从终端粘贴或从配置文件读取的 Base64 字符串通常带有尾部换行符。基本解码器会拒绝 Base64 字符集之外的任何字符。

解决方案: 在解码前调用 .strip() 去除空白,或换用 getMimeDecoder()——它会忽略空白字符。

Before · Java
After · Java
// Read from environment variable — has a trailing newline
String encoded = System.getenv("DB_PASSWORD_B64"); // "cG9zdGdyZXM=
"
byte[] decoded = Base64.getDecoder().decode(encoded);
// IllegalArgumentException: Illegal base64 character a
String encoded = System.getenv("DB_PASSWORD_B64");
byte[] decoded = Base64.getDecoder().decode(encoded.strip());
System.out.println(new String(decoded, StandardCharsets.UTF_8));
// postgres
将二进制数据转换为 String

问题: 对二进制内容(图像、protobuf、加密 blob)调用 new String(decoded) 会产生无效的 String。之后再转回字节时会悄无声息地损坏数据,因为 String 构造函数会替换无效的 UTF-8 序列。

解决方案: 在整个处理流程中将二进制数据保持为 byte[]。只有在确认内容为文本时才转换为 String。

Before · Java
After · Java
byte[] decoded = Base64.getDecoder().decode(pngBase64);
String imageStr = new String(decoded); // corrupts binary
Files.writeString(Path.of("image.png"), imageStr); // broken file
byte[] decoded = Base64.getDecoder().decode(pngBase64);
// Write bytes directly — no String conversion
Files.write(Path.of("image.png"), decoded);

方法对比

内置解码器涵盖大多数使用场景。Apache Commons Codec 和 Guava 是可能在旧代码库中遇到的替代方案。

方法
编码变体
忽略空白字符
流式处理
自定义类型
需要安装
Base64.getDecoder()
标准(+, /)
否(JDK 8+)
Base64.getUrlDecoder()
URL 安全(-, _)
否(JDK 8+)
Base64.getMimeDecoder()
MIME(允许换行)
否(JDK 8+)
decoder.wrap(InputStream)
任意变体
取决于解码器
否(JDK 8+)
Apache Commons Base64InputStream
标准 / URL 安全
是(commons-codec)
Apache Commons Base64.decodeBase64()
标准
是(commons-codec)
Guava BaseEncoding.base64().decode()
标准
是(guava)

JWT 令牌和现代 API 载荷:使用 getUrlDecoder()。邮件附件和 PEM 证书: 使用 getMimeDecoder()。内存敏感的大文件:使用 decoder.wrap(InputStream)。 其他情况:使用 getDecoder()。只有当 Apache Commons Codec 已经在你的依赖树中时, 才考虑使用它。

开发过程中如需快速验证, 在线 Base64 解码器 比编写一次性的类更快。

常见问题

如何在 Java 中解码 Base64 字符串?

导入 java.util.Base64,调用 Base64.getDecoder().decode(encodedString)。方法返回 byte[]——用 new String(bytes, StandardCharsets.UTF_8) 包装即可得到可读文本。对于 URL 安全的 Base64(JWT 使用此格式),将 getDecoder() 换成 getUrlDecoder()。

Java 8+
import java.util.Base64;
import java.nio.charset.StandardCharsets;

byte[] decoded = Base64.getDecoder().decode("c2VydmVyLWNvbmZpZw==");
String result = new String(decoded, StandardCharsets.UTF_8);
System.out.println(result); // server-config

Java 中 getDecoder() 和 getMimeDecoder() 有什么区别?

getDecoder() 是严格模式——它会拒绝 Base64 字符集之外的任何字符,包括换行符。getMimeDecoder() 则容忍换行符(\r\n)并忽略任何非 Base64 字符,因此它是解码邮件附件和 PEM 证书的正确选择——这些数据通常在每 76 个字符处换行。

Java 8+
String wrapped = "c2VydmVyLWNv\r\nbmZpZw==";

// getDecoder() throws IllegalArgumentException
// Base64.getDecoder().decode(wrapped); // FAILS

// getMimeDecoder() handles it
byte[] decoded = Base64.getMimeDecoder().decode(wrapped);
System.out.println(new String(decoded)); // server-config

如何在 Java 中解码 URL 安全的 Base64 字符串?

使用 Base64.getUrlDecoder().decode(encoded)。URL 解码器期望 RFC 4648 §5 定义的 - 和 _ 字符集,而非 + 和 /。JWT 令牌始终使用此字符集。如果填充字符(=)被去除(JWT 中常见),URL 解码器仍然可以处理——Java 的 URL 解码器同时支持带填充和不带填充的输入。

Java 8+
import java.util.Base64;

// JWT header — URL-safe, no padding
String jwtHeader = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
byte[] decoded = Base64.getUrlDecoder().decode(jwtHeader);
System.out.println(new String(decoded));
// {"alg":"HS256","typ":"JWT"}

如何在 Java 中流式解码大型 Base64 文件?

使用 decoder.wrap(inputStream) 包装 FileInputStream。返回的 InputStream 在读取字节时动态解码 Base64,因此内存占用保持恒定,与文件大小无关。将其通过 BufferedInputStream 传输,或直接传给 Files.copy() 以获得最佳吞吐量。

Java 8+
import java.util.Base64;
import java.io.*;
import java.nio.file.*;

try (InputStream in = Base64.getDecoder().wrap(
        new BufferedInputStream(new FileInputStream("payload.b64")));
     OutputStream out = new FileOutputStream("payload.bin")) {
    in.transferTo(out);
}

为什么 Base64.getDecoder().decode() 会抛出 IllegalArgumentException?

基本解码器是严格的:它拒绝换行符、空格以及 A-Za-z0-9+/= 之外的任何字符。三种常见原因:输入有尾部换行符(调用 trim 去除),输入使用了 URL 安全字符如 - 和 _(换用 getUrlDecoder()),或者输入在 76 个字符处换行(换用 getMimeDecoder())。如果错误信息不明确,建议直接检查原始字节。

Java 8+
String raw = "c2VydmVyLWNvbmZpZw==\n"; // trailing newline

// Option 1: trim whitespace
byte[] decoded = Base64.getDecoder().decode(raw.strip());

// Option 2: use MIME decoder which ignores whitespace
byte[] decoded2 = Base64.getMimeDecoder().decode(raw);

在 Java 中能否不使用 java.util.Base64 来解码 Base64?

可以,但在 Java 8+ 上没有充分的理由这样做。Java 8 之前,开发者使用 sun.misc.BASE64Decoder(内部 API,已在 Java 9+ 中移除)、javax.xml.bind.DatatypeConverter.parseBase64Binary()(已在 Java 11 中移除)或 Apache Commons Codec。这三种方式要么已弃用,要么需要额外依赖。建议坚持使用 java.util.Base64——它更快,随 JDK 一起提供,且涵盖全部三种变体(基本、URL 安全、MIME)。

相关工具

  • Base64 Encoder — 在浏览器中将文本或二进制数据编码为 Base64,便于生成测试数据并粘贴到 Java 单元测试中。
  • JWT Decoder — 一次性分割并解码 JWT 的三个段,逐字段检查载荷内容——比只需要读取令牌时编写 Java 类要快得多。
  • URL Decoder — 对 URL 编码的字符串进行百分号解码,在 API 响应将 Base64url 数据与百分号编码的查询参数混合使用时非常有用。
  • JSON Formatter — 解码 Base64 JWT 载荷或 API 配置后,将 JSON 粘贴到这里进行格式化并验证结构。
PN
Pavel NovakBackend Engineer

Pavel is a backend engineer with deep roots in the JVM ecosystem, working primarily with Java and Kotlin. He has extensive experience building data-intensive services and integrating third-party APIs at scale. He writes about modern Java features, the Jackson ecosystem, serialisation patterns, and practical approaches to keeping large codebases maintainable.

AO
Aisha Osei技术审阅者

Aisha is a Java engineer specialising in application security, Spring Security, and API design. She has worked on identity and access management systems, OAuth 2.0 integrations, and microservice security at scale. She writes about secure Java coding practices, token validation, cryptographic utilities, and the Spring ecosystem from a security-first perspective.