杰瑞科技汇

Java 二进制与 Base64 如何转换?

  1. 什么是二进制数据? (在 Java 中的体现)
  2. 什么是 Base64?
  3. Java 8 及更高版本 (推荐):使用 java.util.Base64
  4. Java 8 之前版本:使用第三方库 (如 Apache Commons Codec)
  5. 完整代码示例
  6. 最佳实践和注意事项

什么是二进制数据? (在 Java 中的体现)

在 Java 中,"二进制数据" 通常不是指一个简单的 byte 原始类型,而是指一个字节数组 (byte[]),无论是图片、音频、视频、压缩文件,还是任何需要在网络上传输的非文本数据,在内存中都是以 byte[] 的形式存在的。

Java 二进制与 Base64 如何转换?-图1
(图片来源网络,侵删)
  • byte[]: 这是最核心的表示方式,从一个文件中读取的内容就是一个 byte[]
  • InputStream / OutputStream: 当处理大文件时,我们不会一次性将所有数据读入内存,而是使用流式处理。InputStream 是二进制数据的来源,OutputStream 是二进制数据的去向。
  • ByteBuffer: 在 NIO (New I/O) 中,ByteBuffer 提供了更灵活的方式来处理二进制数据。

什么是 Base64?

Base64 是一种用 64 个可打印字符来表示任意二进制数据的方法,它常用于:

  • 在文本协议中传输二进制数据:因为很多系统(如 HTTP、Email)最初只设计来处理文本(ASCII),直接传输二进制数据(如 0x000xFF)可能会导致问题,Base64 将二进制数据转换成纯文本,可以安全地嵌入到这些协议中。
  • 简单的数据编码:它不是加密,而是一种编码,所以任何人都可以解码,它只是让数据变得“可打印”。

工作原理简述

  1. 将 3 个字节 (24 位) 的二进制数据作为一组。
  2. 将这 24 位重新划分为 4 组,每组 6 位。
  3. 将这 4 个 6 位的值分别作为索引,去查一个包含 64 个字符的表(A-Z, a-z, 0-9, '+', '/')。
  4. 得到 4 个 Base64 字符。
  5. 如果输入的字节数不是 3 的倍数,会用 进行填充。

Java 8 及更高版本 (推荐)

从 Java 8 开始,标准库中正式引入了 java.util.Base64 类,它提供了简单、高效且标准的方式来处理 Base64 编码和解码。这是目前推荐使用的方式。

java.util.Base64 类的核心方法

这个类提供了 getEncoder(), getDecoder(), getMimeEncoder(), getMimeDecoder() 等静态方法来获取不同的编解码器。

Java 二进制与 Base64 如何转换?-图2
(图片来源网络,侵删)
  • Base64.getEncoder(): 标准的 Base64 编码器。
  • Base64.getDecoder(): 标准的 Base64 解码器。
  • Base64.getMimeEncoder(): MIME 友好的 Base64 编码器,它会在每 76 个字符后插入一个换行符 \n,这符合 MIME (Multipurpose Internet Mail Extensions) 标准,适合在 Email 等场景使用。
  • Base64.getMimeDecoder(): MIME 友好的 Base64 解码器。

核心方法

  • 编码:
    • byte[] encode(byte[] src): 编码字节数组。
    • String encodeToString(byte[] src): 编码字节数组并直接返回一个字符串。
    • OutputStream wrap(OutputStream os): 返回一个 OutputStream,写入其中的任何数据都会被自动 Base64 编码。
  • 解码:
    • byte[] decode(byte[] src): 解码 Base64 编码的字节数组。
    • byte[] decode(String src): 解码 Base64 编码的字符串。
    • InputStream wrap(InputStream is): 返回一个 InputStream,从中读取的任何数据都会被自动 Base64 解码。

Java 8 之前版本

如果你在使用 Java 7 或更早的版本,标准库中没有 Base64 支持,这时,最常用、最可靠的选择是 Apache Commons Codec 库。

如何添加依赖 (Maven)

<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.15</version> <!-- 使用最新的稳定版本 -->
</dependency>

Apache Commons Codec 的核心类

核心类是 org.apache.commons.codec.binary.Base64

  • 编码:
    • static byte[] encodeBase64(byte[] binaryData): 编码字节数组。
    • static String encodeBase64String(byte[] binaryData): 编码字节数组并返回字符串。
  • 解码:
    • static byte[] decodeBase64(byte[] base64Data): 解码 Base64 字节数组。
    • static byte[] decodeBase64(String base64String): 解码 Base64 字符串。

完整代码示例

下面我们通过一个完整的 Java 8 示例来演示所有核心操作。

示例:文件 <-> 字节数组 <-> Base64 字符串

这个例子展示了如何读取一个图片文件,将其编码为 Base64 字符串,然后再将字符串解码并写回到一个新的文件中。

Java 二进制与 Base64 如何转换?-图3
(图片来源网络,侵删)
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;
public class BinaryBase64Example {
    public static void main(String[] args) {
        // 假设我们有一个名为 "sample.png" 的图片文件在项目根目录下
        String originalFilePath = "sample.png";
        String encodedFilePath = "encoded.txt";
        String decodedFilePath = "decoded_copy.png";
        try {
            // 1. 从文件读取二进制数据 (字节数组)
            byte[] fileBytes = readAllBytesOrExit(originalFilePath);
            System.out.println("原始文件大小: " + fileBytes.length + " 字节");
            // 2. 将字节数组编码为 Base64 字符串
            // 使用标准编码器
            Base64.Encoder encoder = Base64.getEncoder();
            String base64String = encoder.encodeToString(fileBytes);
            System.out.println("Base64 编码后的字符串 (前 50 个字符): " + base64String.substring(0, 50) + "...");
            System.out.println("Base64 字符串长度: " + base64String.length());
            // 将 Base64 字符串保存到文件中
            Files.write(Paths.get(encodedFilePath), base64String.getBytes());
            System.out.println("Base64 字符串已保存到: " + encodedFilePath);
            // 3. 从文件读取 Base64 字符串
            String base64FromFile = new String(Files.readAllBytes(Paths.get(encodedFilePath)));
            // 4. 将 Base64 字符串解码为字节数组
            Base64.Decoder decoder = Base64.getDecoder();
            byte[] decodedBytes = decoder.decode(base64FromFile);
            System.out.println("解码后的字节数组大小: " + decodedBytes.length + " 字节");
            // 5. 将解码后的字节数组写回到新文件
            Files.write(Paths.get(decodedFilePath), decodedBytes);
            System.out.println("解码后的文件已保存到: " + decodedFilePath);
            System.out.println("操作完成!请检查 'decoded_copy.png' 是否与 'sample.png' 一致。");
        } catch (IOException e) {
            System.err.println("处理文件时发生错误: " + e.getMessage());
            e.printStackTrace();
        }
    }
    /**
     * 一个简单的辅助方法,用于读取文件的所有字节。
     * 如果文件不存在,则打印错误信息并退出程序。
     */
    private static byte[] readAllBytesOrExit(String filePath) throws IOException {
        Path path = Paths.get(filePath);
        if (!Files.exists(path)) {
            System.err.println("错误: 文件 '" + filePath + "' 不存在!");
            System.exit(1);
        }
        return Files.readAllBytes(path);
    }
}

示例:使用流处理大文件

对于非常大的文件,将整个文件读入内存可能会导致 OutOfMemoryError,这时,使用 Base64.getEncoder().wrap()Base64.getDecoder().wrap() 进行流式处理是最佳选择。

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
public class StreamBase64Example {
    public static void main(String[] args) {
        String largeFilePath = "large_file.zip"; // 假设这是一个大文件
        String encodedStreamFilePath = "large_encoded.txt";
        String decodedStreamFilePath = "large_decoded.zip";
        try {
            // 编码:文件 -> InputStream -> Base64 OutputStream -> 文件
            encodeFile(largeFilePath, encodedStreamFilePath);
            System.out.println("大文件流式编码完成。");
            // 解码:Base64 文件 -> InputStream -> Base64 InputStream -> 文件
            decodeFile(encodedStreamFilePath, decodedStreamFilePath);
            System.out.println("大文件流式解码完成。");
        } catch (IOException e) {
            System.err.println("流处理时发生错误: " + e.getMessage());
            e.printStackTrace();
        }
    }
    private static void encodeFile(String sourcePath, String targetPath) throws IOException {
        try (InputStream is = Files.newInputStream(Paths.get(sourcePath));
             OutputStream os = Files.newOutputStream(Paths.get(targetPath))) {
            // 创建一个 Base64 编码输出流,所有写入 os 的数据都会被自动编码
            Base64.Encoder encoder = Base64.getEncoder();
            OutputStream encodedOs = encoder.wrap(os);
            // 使用缓冲流提高性能
            byte[] buffer = new byte[8192]; // 8KB 缓冲区
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                encodedOs.write(buffer, 0, bytesRead);
            }
            // encodedOs.close() 会在 try-with-resources 中自动调用
        }
    }
    private static void decodeFile(String sourcePath, String targetPath) throws IOException {
        try (InputStream is = Files.newInputStream(Paths.get(sourcePath));
             OutputStream os = Files.newOutputStream(Paths.get(targetPath))) {
            // 创建一个 Base64 解码输入流,所有从 is 读取的数据都会被自动解码
            Base64.Decoder decoder = Base64.getDecoder();
            InputStream decodedIs = decoder.wrap(is);
            // 使用缓冲流提高性能
            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = decodedIs.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            // decodedIs.close() 会在 try-with-resources 中自动调用
        }
    }
}

最佳实践和注意事项

  1. 首选 Java 8+ java.util.Base64:如果你的项目使用 Java 8 或更高版本,请务必使用标准库,它不需要引入外部依赖,并且性能和可靠性都经过了充分验证。

  2. 注意数据增长:Base64 编码会使数据大小增加约 33%,因为每 3 个字节(24位)会变成 4 个字符,这在处理大文件时需要考虑存储和网络带宽成本。

  3. 区分编码和解码

    • 编码byte[] (二进制) -> String (Base64 文本)
    • 解码String (Base64 文本) -> byte[] (二进制)
  4. MIME vs 标准

    • 如果你的 Base64 数据需要嵌入到 Email (MIME 消息) 或需要符合某些 Web 标准,请使用 getMimeEncoder()getMimeDecoder()
    • 如果只是简单的数据交换,使用标准的 getEncoder()getDecoder() 即可。
  5. 安全性:再次强调,Base64 是编码,不是加密,它不能保护数据的机密性,如果数据是敏感的,必须先进行加密(如使用 AES),然后再对加密后的密文进行 Base64 编码。

  6. 处理异常:在进行 Base64 解码时,如果输入的字符串格式不正确(包含非法字符或填充不正确),decode 方法会抛出 IllegalArgumentException,务必做好异常处理。

分享:
扫描分享到社交APP
上一篇
下一篇