Mã hóa Base64 trong Java — Ví dụ java.util.Base64

·Java Security & API Engineer·Đánh giá bởiPavel Novak·Đã xuất bản

Sử dụng Bộ Mã Hóa Base64 Trực Tuyến miễn phí trực tiếp trên trình duyệt — không cần cài đặt.

Dùng thử Bộ Mã Hóa Base64 Trực Tuyến trực tuyến →

Mỗi khi tôi cần thiết lập header HTTP Basic Auth, nhúng certificate vào Kubernetes secret, hay đẩy dữ liệu nhị phân qua JSON API, bước đầu tiên luôn giống nhau: mã hóa Base64 các byte thô thành chuỗi an toàn ASCII. Java làm điều này rất đơn giản với java.util.Base64, API tiêu chuẩn có từ Java 8 thay thế cho sun.misc.BASE64Encoder đã bị deprecated. Để mã hóa nhanh một lần mà không cần viết code, Base64 Encoder của ToolDeck xử lý tức thì ngay trên trình duyệt. Hướng dẫn này bao gồm Base64.getEncoder(), getUrlEncoder(), getMimeEncoder(), mã hóa tệp, streaming với wrap(OutputStream), và các lỗi thường gặp ngay cả với developer Java có kinh nghiệm. Tất cả ví dụ đều biên dịch được trên Java 8 đến Java 21+.

  • Base64.getEncoder().encodeToString(bytes) là one-liner tiêu chuẩn — tích hợp sẵn trong JDK từ Java 8, không thay đổi qua Java 17 và 21.
  • Luôn truyền StandardCharsets.UTF_8 vào String.getBytes() trước khi mã hóa — bỏ qua sẽ dùng encoding mặc định của nền tảng, vốn khác nhau giữa các JVM.
  • getUrlEncoder() tạo đầu ra URL-safe (- thay cho +, _ thay cho /) và withoutPadding() loại bỏ các ký tự = ở cuối.
  • getMimeEncoder() chèn ngắt dòng sau mỗi 76 ký tự — bắt buộc với định dạng email (MIME) và certificate PEM.
  • Với tệp lớn, dùng Base64.getEncoder().wrap(OutputStream) để streaming mà không cần tải toàn bộ tệp vào bộ nhớ.

Mã hóa Base64 là gì?

Base64 chuyển đổi dữ liệu nhị phân tùy ý thành chuỗi gồm 64 ký tự ASCII in được: A-Z, a-z, 0-9, + /. Cứ mỗi 3 byte đầu vào tạo ra đúng 4 ký tự Base64. Nếu độ dài đầu vào không chia hết cho 3, một hoặc hai ký tự đệm = được thêm vào. Đầu ra đã mã hóa luôn lớn hơn dữ liệu gốc khoảng 33%.

Base64 không phải là mã hóa bảo mật. Bất kỳ ai có chuỗi đã mã hóa đều có thể giải mã nó. Mục đích của nó là đảm bảo an toàn trong quá trình truyền tải: HTTP headers, JSON payload, tài liệu XML và nội dung email là các giao thức dựa trên văn bản không thể mang byte nhị phân thô mà không bị hỏng dữ liệu. Các trường hợp dùng phổ biến trong Java bao gồm HTTP Basic Authentication, nhúng certificate PEM, lưu dữ liệu nhị phân vào cột văn bản trong cơ sở dữ liệu, và xây dựng các đoạn JWT token.

Before · text
After · text
deploy-svc:sk_live_4eC39HqLyjWDarjtT1zdp7dc
ZGVwbG95LXN2Yzpza19saXZlXzRlQzM5SHFMeWpXRGFyanRUMXpkcDdkYw==

Base64.getEncoder().encodeToString() — API Tiêu Chuẩn

java.util.Base64 được giới thiệu trong Java 8 như là bản thay thế chính thức cho sun.misc.BASE64Encoder. Class này cung cấp ba factory method tĩnh — mỗi phương thức trả về một instance của class lồng Base64.Encoder — bao gồm ba biến thể Base64 được định nghĩa trong RFC 4648. Không cần thư viện bên thứ ba. Không cần phụ thuộc Maven. Chỉ cần import và gọi.

Ví dụ Tối Giản — Mã Hóa Chuỗi

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

public class EncodeDemo {
    public static void main(String[] args) {
        String credentials = "monitoring-svc:9f2a7c4e-b1d8-4a3f";
        byte[] credentialBytes = credentials.getBytes(StandardCharsets.UTF_8);

        String encoded = Base64.getEncoder().encodeToString(credentialBytes);
        System.out.println(encoded);
        // bW9uaXRvcmluZy1zdmM6OWYyYTdjNGUtYjFkOC00YTNm
    }
}

Bước quan trọng mà hầu hết developer Java bỏ qua lần đầu: một String phải được chuyển đổi sang byte[] trước khi mã hóa. Base64 hoạt động trên bytes, không phải ký tự. encodeToString() nhận một byte[] và trả về String Base64 trực tiếp. Nếu bạn cần kết quả mã hóa dưới dạng bytes thay vì vậy, dùng encode(byte[]) — phương thức này trả về byte[] của các ký tự Base64 được mã hóa ASCII, hữu ích khi bạn đang ghi trực tiếp vào OutputStream hoặc xây dựng các frame giao thức nhị phân.

HTTP Basic Auth — Trường Hợp Dùng Phổ Biến Nhất

HTTP Basic Authentication có lẽ là lý do phổ biến nhất mà developer Java dùng đến mã hóa Base64. Đặc tả (RFC 7617) yêu cầu chuỗi thông tin xác thực username:password phải được mã hóa Base64 và đặt trong Authorization header. Tôi đã thấy cách làm sai nhiều lần — thường là do quên dấu hai chấm ngăn cách hoặc mã hóa từng thành phần riêng lẻ.

Java — HTTP Basic Auth header
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Base64;
import java.nio.charset.StandardCharsets;

public class BasicAuthExample {
    public static void main(String[] args) throws Exception {
        String username = "metrics-exporter";
        String apiKey = "sk_live_4eC39HqLyjWDarjtT1zdp7dc";

        // username:password → Base64
        String credentials = username + ":" + apiKey;
        String authHeader = "Basic " + Base64.getEncoder()
            .encodeToString(credentials.getBytes(StandardCharsets.UTF_8));

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.example.com/v2/metrics"))
            .header("Authorization", authHeader)
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());

        System.out.println(response.statusCode());  // 200
    }
}

Round-Trip — Mã Hóa và Giải Mã

Java 8+ — encode and decode round-trip
import java.util.Base64;
import java.nio.charset.StandardCharsets;

public class RoundTrip {
    public static void main(String[] args) {
        String original = "X-Correlation-ID: req_8a4f2c91-e7b3-4d56-9012-3f7a8b9c0d1e";

        // Mã hóa
        String encoded = Base64.getEncoder()
            .encodeToString(original.getBytes(StandardCharsets.UTF_8));
        System.out.println(encoded);
        // WC1Db3JyZWxhdGlvbi1JRDogcmVxXzhhNGYyYzkxLWU3YjMtNGQ1Ni05MDEyLTNmN2E4YjljMGQxZQ==

        // Giải mã
        byte[] decodedBytes = Base64.getDecoder().decode(encoded);
        String decoded = new String(decodedBytes, StandardCharsets.UTF_8);

        System.out.println(original.equals(decoded));  // true
    }
}
Lưu ý:API java.util.Base64 giống hệt nhau từ Java 8 đến Java 17 và Java 21. Không cần di chuyển khi nâng cấp JDK. Code tương tự biên dịch và chạy trên mọi phiên bản từ Java 8.

Mã Hóa Dữ Liệu Không Phải String — byte[], UUID và Timestamp

Mã hóa Base64 trong Java luôn bắt đầu với một byte[]. String chuyển đổi qua getBytes(StandardCharsets.UTF_8), nhưng các kiểu khác cần bước chuyển đổi trước. UUID, timestamp và định danh số phải được serialize thành dạng string hoặc byte trước khi có thể mã hóa Base64.

UUID — Mã Hóa Dưới Dạng String

Java — Base64 encoding a UUID
import java.util.Base64;
import java.util.UUID;
import java.nio.charset.StandardCharsets;

UUID sessionId = UUID.fromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8");
String encoded = Base64.getEncoder()
    .encodeToString(sessionId.toString().getBytes(StandardCharsets.UTF_8));
System.out.println(encoded);
// NmJhN2I4MTAtOWRhZC0xMWQxLTgwYjQtMDBjMDRmZDQzMGM4

UUID Compact — Mã Hóa 16 Byte Thô

Nếu bạn muốn kết quả mã hóa ngắn hơn, hãy trích xuất 128 bit của UUID thành 16 byte thô thay vì chuyển đổi sang dạng string 36 ký tự. Đầu ra Base64 giảm từ 48 ký tự xuống còn 24.

Java — compact UUID encoding
import java.nio.ByteBuffer;
import java.util.Base64;
import java.util.UUID;

UUID eventId = UUID.fromString("550e8400-e29b-41d4-a716-446655440000");

ByteBuffer buffer = ByteBuffer.wrap(new byte[16]);
buffer.putLong(eventId.getMostSignificantBits());
buffer.putLong(eventId.getLeastSignificantBits());

String compact = Base64.getUrlEncoder()
    .withoutPadding()
    .encodeToString(buffer.array());
System.out.println(compact);
// VQ6EAOKbQdSnFkRmVUQAAA
// 22 ký tự so với 48 ký tự của cách dùng string

Timestamp và Payload Hỗn Hợp

Java — encoding a JSON-like payload with timestamp
import java.time.Instant;
import java.util.Base64;
import java.nio.charset.StandardCharsets;

// Mô phỏng payload kiểu JWT
String payload = String.format(
    "{"sub":"usr_7b3c","iss":"auth.internal","iat":%d,"exp":%d}",
    Instant.now().getEpochSecond(),
    Instant.now().plusSeconds(3600).getEpochSecond()
);

String encoded = Base64.getUrlEncoder()
    .withoutPadding()
    .encodeToString(payload.getBytes(StandardCharsets.UTF_8));
System.out.println(encoded);
// eyJzdWIiOiJ1c3JfN2IzYyIsImlzcyI6ImF1dGguaW50ZXJuYWwiLCJpYXQiOj... (URL-safe, không có padding)
Cảnh báo:Không gọi toString() trên byte[] và mong đợi nội dung của nó — phương thức đó trả về identity hash của mảng như [B@6d06d69c. Hãy dùng new String(bytes, StandardCharsets.UTF_8) hoặc truyền mảng byte trực tiếp vào encodeToString().

Tài Liệu Tham Khảo Phương Thức Base64.Encoder

Class java.util.Base64 cung cấp ba factory method, mỗi phương thức trả về một Base64.Encoder được cấu hình cho một biến thể cụ thể. Các instance encoder là thread-safe và stateless — tạo một lần và dùng lại.

Phương thức
Kiểu trả về
Mô tả
getEncoder()
Base64.Encoder
Trả về encoder chuẩn RFC 4648 sử dụng bảng chữ cái tiêu chuẩn (A-Z, a-z, 0-9, +, /)
getUrlEncoder()
Base64.Encoder
Trả về encoder sử dụng bảng chữ cái URL-safe (- thay cho +, _ thay cho /)
getMimeEncoder()
Base64.Encoder
Trả về encoder MIME chèn ngắt dòng \r\n sau mỗi 76 ký tự
getMimeEncoder(lineLength, lineSeparator)
Base64.Encoder
Encoder MIME với độ dài dòng và byte phân cách tùy chỉnh
encoder.withoutPadding()
Base64.Encoder
Trả về encoder bỏ qua các ký tự đệm = ở cuối
encoder.encode(byte[])
byte[]
Mã hóa mảng byte, trả về mảng byte đã mã hóa
encoder.encodeToString(byte[])
String
Mã hóa mảng byte, trả về String Base64 trực tiếp
encoder.wrap(OutputStream)
OutputStream
Bọc OutputStream để mã hóa Base64 theo luồng dữ liệu

Base64.getUrlEncoder() — Mã Hóa URL-Safe

Encoder URL-safe sử dụng bảng chữ cái thay thế trong đó + trở thành - / trở thành _, theo định nghĩa trong RFC 4648 Section 5. Điều này quan trọng khi chuỗi Base64 xuất hiện trong tham số truy vấn URL, tên tệp, hoặc giá trị cookie — các ký tự Base64 chuẩn xung đột với dấu phân cách URL và các ký tự đặc biệt của hệ thống tệp.

Java — URL-safe Base64 encoding
import java.util.Base64;
import java.nio.charset.StandardCharsets;

String redirectUri = "https://app.internal/callback?state=auth_pending&nonce=9f2a7c";
byte[] data = redirectUri.getBytes(StandardCharsets.UTF_8);

// Encoder chuẩn — chứa + và / làm hỏng URL
String standard = Base64.getEncoder().encodeToString(data);
System.out.println(standard);
// aHR0cHM6Ly9hcHAuaW50ZXJuYWwvY2FsbGJhY2s/c3RhdGU9YXV0aF9wZW5kaW5nJm5vbmNlPTlmMmE3Yw==

// Encoder URL-safe — an toàn cho tham số truy vấn và tên tệp
String urlSafe = Base64.getUrlEncoder().encodeToString(data);
System.out.println(urlSafe);
// aHR0cHM6Ly9hcHAuaW50ZXJuYWwvY2FsbGJhY2s_c3RhdGU9YXV0aF9wZW5kaW5nJm5vbmNlPTlmMmE3Yw==

// URL-safe không có padding — cho JWT và token compact
String noPadding = Base64.getUrlEncoder().withoutPadding().encodeToString(data);
System.out.println(noPadding);
// aHR0cHM6Ly9hcHAuaW50ZXJuYWwvY2FsbGJhY2s_c3RhdGU9YXV0aF9wZW5kaW5nJm5vbmNlPTlmMmE3Yw

Biến thể withoutPadding() loại bỏ các ký tự = ở cuối. Đặc tả JWT yêu cầu Base64 URL-safe không có padding cho các đoạn header và payload, vì vậy getUrlEncoder().withoutPadding() chính xác là lệnh gọi bạn cần khi xây dựng hoặc xử lý JWT token thủ công.

Lưu ý:Phương thức withoutPadding() trả về một instance encoder mới — nó không sửa đổi encoder gốc. Cả hai đều có thể được gán cho các trường static final và dùng lại an toàn giữa các thread.

Mã Hóa Từ Tệp và Phản Hồi API

Hai tình huống thực tế phổ biến nhất khi mã hóa Base64 trong Java: đọc tệp nhị phân từ đĩa (certificate, hình ảnh, gói cấu hình) và mã hóa dữ liệu nhận được từ phản hồi HTTP.

Mã Hóa Tệp Sang Base64

Java — encoding a file
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;

public class FileEncoder {
    public static void main(String[] args) {
        try {
            byte[] fileBytes = Files.readAllBytes(Path.of("certs/server.pem"));
            String encoded = Base64.getEncoder().encodeToString(fileBytes);

            System.out.printf("Gốc: %d bytes%n", fileBytes.length);
            System.out.printf("Đã mã hóa:  %d ký tự%n", encoded.length());

            // Ghi nội dung đã mã hóa vào tệp văn bản
            Files.writeString(
                Path.of("certs/server.pem.b64"),
                encoded
            );
        } catch (java.io.IOException e) {
            System.err.println("Không đọc được tệp: " + e.getMessage());
        }
    }
}

Mã Hóa Body Phản Hồi API

Java 11+ — encoding an HTTP response
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Base64;

public class ApiEncoder {
    public static void main(String[] args) {
        try {
            HttpClient client = HttpClient.newHttpClient();
            HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://api.example.com/v2/reports/weekly.pdf"))
                .header("Authorization", "Bearer tok_8f2a9c3d")
                .build();

            HttpResponse<byte[]> response = client.send(
                request, HttpResponse.BodyHandlers.ofByteArray()
            );

            if (response.statusCode() == 200) {
                String encoded = Base64.getEncoder()
                    .encodeToString(response.body());
                System.out.printf("Đã mã hóa %d bytes → %d ký tự%n",
                    response.body().length, encoded.length());
            } else {
                System.err.printf("HTTP %d: %s%n",
                    response.statusCode(),
                    new String(response.body()));
            }
        } catch (Exception e) {
            System.err.println("Yêu cầu thất bại: " + e.getMessage());
        }
    }
}

Lưu ý nhanh trước phần CLI: nếu bạn chỉ cần dán tệp hoặc phản hồi API và nhận đầu ra Base64 mà không cần viết code, công cụ Base64 Encoder trực tuyến hỗ trợ cả đầu vào văn bản và nhị phân.

Mã Hóa Base64 Qua Dòng Lệnh

Đôi khi bạn chỉ cần mã hóa một chuỗi hoặc tệp từ terminal — không cần project Java, không cần IDE, không cần bước build. Hầu hết các hệ thống Unix đều có sẵn lệnh base64, và nếu bạn đã cài JDK, bạn có thể dùng jshell theo cách tiếp cận thuần Java.

Bash — command-line Base64 encoding
# macOS / Linux — mã hóa một chuỗi
echo -n "deploy-bot:sk_prod_9f2a7c4e" | base64
# ZGVwbG95LWJvdDpza19wcm9kXzlmMmE3YzRl

# Mã hóa tệp
base64 < certs/server.pem > certs/server.pem.b64

# Dùng jshell (JDK 9+)
echo 'System.out.println(java.util.Base64.getEncoder().encodeToString("deploy-bot:sk_prod_9f2a7c4e".getBytes()))' | jshell -

# Dùng java trực tiếp với one-liner
java -e 'System.out.println(java.util.Base64.getEncoder().encodeToString(args[0].getBytes()))' "my-secret"
# Lưu ý: java -e yêu cầu JDK 23+ (JEP 477)

Cách dùng jshell đặc biệt hữu ích khi bạn cần xác minh rằng code Java của mình tạo ra cùng đầu ra với công cụ Unix, hoặc khi bạn đang debug sự không khớp giữa những gì service của bạn gửi đi và những gì phía nhận mong đợi. Tôi đặt shell alias cho nó.

Lưu ý:Trên macOS, lệnh base64 dùng -D để giải mã. Trên Linux (GNU coreutils) dùng -d. Hành vi mã hóa giống hệt nhau trên cả hai. Flag -w 0 trên Linux tắt ngắt dòng trong đầu ra, thường là điều bạn muốn khi pipe sang các lệnh khác.

Apache Commons Codec — Giải Pháp Thay Thế Hiệu Suất Cao

Với hầu hết ứng dụng, java.util.Base64 đã đủ nhanh. Nhưng nếu bạn đang xử lý hàng triệu thao tác mã hóa trong vòng lặp chặt — hãy nghĩ đến pipeline nhập log hoặc message broker thông lượng cao — Apache Commons Codec đáng để benchmark. Thư viện này đã tồn tại từ lâu trước Java 8 và cung cấp giải pháp thay thế đã được kiểm chứng với bề mặt API hơi khác.

Java — Apache Commons Codec
// Maven: org.apache.commons:commons-codec:1.17.0
import org.apache.commons.codec.binary.Base64;
import java.nio.charset.StandardCharsets;

byte[] telemetryPayload = ("{"service":"metrics-collector","
    + ""host":"prod-east-07","
    + ""cpu_pct":72.4,"
    + ""mem_mb":3891,"
    + ""timestamp":1710523200}")
    .getBytes(StandardCharsets.UTF_8);

// Mã hóa chuẩn
String encoded = Base64.encodeBase64String(telemetryPayload);

// Mã hóa URL-safe
String urlSafe = Base64.encodeBase64URLSafeString(telemetryPayload);

// Kiểm tra chuỗi có phải Base64 hợp lệ không
boolean valid = Base64.isBase64(encoded);
System.out.println(valid);  // true

Apache Commons Codec còn cung cấp Base64OutputStream Base64InputStream cho các tình huống streaming, và bao gồm phương thức xác thực mà JDK encoder thiếu. Nếu Commons Codec đã có trong dependency tree của bạn (nó đi kèm với nhiều dự án Apache), không có lý do gì để không dùng nó.

Guava BaseEncoding

Thư viện Guava của Google bao gồm BaseEncoding cung cấp API fluent cho Base64 với dấu phân cách dòng có thể cấu hình, kiểm soát padding, và hỗ trợ cả bảng chữ cái chuẩn lẫn URL-safe. API đọc rõ ràng, nhưng thêm Guava (khoảng 3 MB) chỉ để mã hóa Base64 là quá mức cần thiết. Nếu Guava đã có trong project của bạn cho các tiện ích collection hoặc caching, API mã hóa là một tính năng bổ sung hữu ích.

Java — Guava BaseEncoding
// Maven: com.google.guava:guava:33.1.0-jre
import com.google.common.io.BaseEncoding;
import java.nio.charset.StandardCharsets;

byte[] webhookPayload = ("{"event":"deployment.completed","
    + ""repo":"payments-api","
    + ""sha":"a7f2c91e4b3d","
    + ""environment":"production"}")
    .getBytes(StandardCharsets.UTF_8);

// Base64 chuẩn
String standard = BaseEncoding.base64().encode(webhookPayload);

// URL-safe
String urlSafe = BaseEncoding.base64Url().encode(webhookPayload);

// Không có padding
String noPad = BaseEncoding.base64Url().omitPadding().encode(webhookPayload);

// Với dấu phân cách dòng (kiểu PEM)
String wrapped = BaseEncoding.base64()
    .withSeparator("\n", 64)
    .encode(webhookPayload);

Base64.getMimeEncoder() — Đầu Ra MIME và PEM Có Ngắt Dòng

Encoder MIME chèn ngắt dòng \r\n sau mỗi 76 ký tự, phù hợp với đặc tả MIME (RFC 2045). Certificate PEM, tệp đính kèm email S/MIME và một số API legacy yêu cầu định dạng này. Encoder chuẩn và URL-safe tạo ra một dòng không ngắt — nếu bạn truyền đầu ra của chúng cho hệ thống mong đợi Base64 có ngắt dòng, nó có thể âm thầm thất bại hoặc từ chối dữ liệu.

Java — MIME Base64 encoding
import java.util.Base64;
import java.nio.charset.StandardCharsets;

// Mô phỏng phần thân certificate PEM
byte[] certData = new byte[256];  // Trong thực tế, đọc từ tệp .der
new java.security.SecureRandom().nextBytes(certData);

// Encoder MIME mặc định — 76 ký tự mỗi dòng, dấu phân cách \r\n
String mimeEncoded = Base64.getMimeEncoder().encodeToString(certData);
System.out.println(mimeEncoded);
// QYx2K3p8Xg7JmN1R+wFkLd...  (76 ký tự)
// Ht5Bv9CzAq0PnSjYl8WxUe...  (76 ký tự)
// ...

// Encoder MIME tùy chỉnh — 64 ký tự mỗi dòng (chuẩn PEM), dấu phân cách \n
Base64.Encoder pemEncoder = Base64.getMimeEncoder(64, new byte[]{'\n'});
String pemBody = pemEncoder.encodeToString(certData);
System.out.println("-----BEGIN CERTIFICATE-----");
System.out.println(pemBody);
System.out.println("-----END CERTIFICATE-----");
Cảnh báo:Không dùng getMimeEncoder() cho JWT token, HTTP header, hoặc tham số URL. Các ngắt dòng sẽ làm hỏng dữ liệu trong những ngữ cảnh đó. Hãy dùng getEncoder() hoặc getUrlEncoder() thay thế.

Streaming Tệp Lớn với Base64.getEncoder().wrap()

Tải toàn bộ tệp vào byte[] bằng Files.readAllBytes() hoạt động tốt với tệp nhỏ, nhưng với tệp trên 50-100 MB bạn có nguy cơ gặp OutOfMemoryError. JDK cung cấp Base64.getEncoder().wrap(OutputStream), trả về một OutputStream mã hóa dữ liệu ngay khi bạn ghi vào nó. Các byte đã mã hóa chảy qua stream bên dưới mà không cần buffer toàn bộ đầu vào.

Java — streaming Base64 encoding
import java.io.*;
import java.nio.file.*;
import java.util.Base64;

public class StreamingEncoder {
    public static void main(String[] args) throws IOException {
        Path inputPath = Path.of("backups/database-export.sql.gz");
        Path outputPath = Path.of("backups/database-export.sql.gz.b64");

        try (
            InputStream in = Files.newInputStream(inputPath);
            OutputStream fileOut = Files.newOutputStream(outputPath);
            OutputStream base64Out = Base64.getEncoder().wrap(fileOut)
        ) {
            byte[] buffer = new byte[8192];
            int bytesRead;
            long totalBytes = 0;

            while ((bytesRead = in.read(buffer)) != -1) {
                base64Out.write(buffer, 0, bytesRead);
                totalBytes += bytesRead;
            }

            System.out.printf("Đã stream %d bytes qua Base64 encoder%n", totalBytes);
        }
        // Đóng base64Out tự động flush các byte padding cuối cùng
    }
}

Khối try-with-resources xử lý việc flush và đóng. Một chi tiết dễ bỏ sót: padding Base64 cuối cùng chỉ được ghi khi OutputStream wrapping được đóng. Nếu bạn quên đóng nó (hoặc chỉ đóng stream ngoài), một vài ký tự cuối của đầu ra mã hóa có thể bị thiếu.

Streaming Tới Network Socket

Phương thức wrap() hoạt động với bất kỳ OutputStream nào — đầu ra tệp, đầu ra socket, body phản hồi HTTP, thậm chí ByteArrayOutputStream. Dưới đây là ví dụ ghi dữ liệu được mã hóa Base64 trực tiếp vào bộ đệm trong bộ nhớ, hữu ích cho unit testing hoặc xây dựng payload để gửi qua HTTP:

Java — streaming to ByteArrayOutputStream
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.Base64;
import java.nio.charset.StandardCharsets;

ByteArrayOutputStream buffer = new ByteArrayOutputStream();

try (OutputStream encoder = Base64.getEncoder().wrap(buffer)) {
    // Ghi dữ liệu theo từng phần — mô phỏng đọc từ stream
    encoder.write("chunk-1:telemetry-data-".getBytes(StandardCharsets.UTF_8));
    encoder.write("chunk-2:more-payload-".getBytes(StandardCharsets.UTF_8));
    encoder.write("chunk-3:final-segment".getBytes(StandardCharsets.UTF_8));
}

String encoded = buffer.toString(StandardCharsets.UTF_8);
System.out.println(encoded);
// Y2h1bmstMTp0ZWxlbWV0cnktZGF0YS1jaHVuay0yOm1vcmUtcGF5bG9hZC1jaHVuay0zOmZpbmFsLXNlZ21lbnQ=

// Xác minh round-trip
byte[] decoded = Base64.getDecoder().decode(encoded);
System.out.println(new String(decoded, StandardCharsets.UTF_8));
// chunk-1:telemetry-data-chunk-2:more-payload-chunk-3:final-segment
Lưu ý:Kích thước buffer trong ví dụ streaming (8192 bytes) không phải ngẫu nhiên. Nó khớp với kích thước buffer mặc định được dùng bởi BufferedInputStream và là sự cân bằng tốt giữa mức sử dụng bộ nhớ và overhead system call. Buffer nhỏ hơn tăng số lần đọc/ghi; buffer lớn hơn lãng phí bộ nhớ mà không cải thiện throughput đáng kể.

Instance Encoder Thread-Safe — Lưu Trữ và Dùng Lại

Base64.Encoder được trả về bởi các factory method là immutable và thread-safe. Gọi Base64.getEncoder() mỗi lần mã hóa tạo ra một object mới. JVM có thể tối ưu hóa điều này, nhưng lưu encoder trong trường static final làm rõ ý định và tránh phân bổ không cần thiết trong các đường dẫn thường xuyên được gọi.

Java — reusable encoder instances
import java.util.Base64;
import java.nio.charset.StandardCharsets;

public class TokenService {
    // Tạo một lần, dùng lại ở mọi nơi — thread-safe
    private static final Base64.Encoder STANDARD = Base64.getEncoder();
    private static final Base64.Encoder URL_SAFE = Base64.getUrlEncoder().withoutPadding();
    private static final Base64.Encoder MIME = Base64.getMimeEncoder();

    public static String encodeForHeader(String value) {
        return STANDARD.encodeToString(value.getBytes(StandardCharsets.UTF_8));
    }

    public static String encodeForUrl(byte[] data) {
        return URL_SAFE.encodeToString(data);
    }

    public static String encodeForEmail(byte[] attachment) {
        return MIME.encodeToString(attachment);
    }
}

Pattern này đặc biệt hữu ích trong các service Spring Boot nơi một utility class xử lý mã hóa cho nhiều controller hoặc phương thức service. Lệnh gọi withoutPadding() trả về một instance encoder mới, vì vậy bạn có thể lưu cả biến thể có padding và không có padding dưới dạng các trường riêng biệt. Mỗi lần gọi encodeToString() hoặc encode() là stateless — không cần đồng bộ hóa, không có trạng thái có thể thay đổi dùng chung.

Các Lỗi Thường Gặp

Gọi getBytes() mà không chỉ định charset

Vấn đề: String.getBytes() không có đối số charset sử dụng encoding mặc định của nền tảng, là windows-1252 trên Windows, UTF-8 trên hầu hết Linux, và khác nhau trên macOS. Cùng một code tạo ra đầu ra Base64 khác nhau trên các máy khác nhau.

Giải pháp: Luôn truyền StandardCharsets.UTF_8 tường minh.

Before · Java
After · Java
String text = "Ключ доступа: prod-east";
byte[] bytes = text.getBytes();  // mặc định của nền tảng — không thể đoán trước
String encoded = Base64.getEncoder().encodeToString(bytes);
String text = "Ключ доступа: prod-east";
byte[] bytes = text.getBytes(StandardCharsets.UTF_8);
String encoded = Base64.getEncoder().encodeToString(bytes);
Dùng encoder chuẩn cho tham số URL

Vấn đề: Base64.getEncoder() xuất ra ký tự + và /. Khi đặt trong chuỗi truy vấn URL, + được hiểu là dấu cách và / là dấu phân cách đường dẫn, làm hỏng giá trị ở phía nhận một cách âm thầm.

Giải pháp: Dùng Base64.getUrlEncoder() cho bất kỳ giá trị nào sẽ xuất hiện trong URL.

Before · Java
After · Java
// Token trong tham số truy vấn URL — sẽ bị lỗi
String token = Base64.getEncoder()
    .encodeToString(sessionData);
String url = "https://auth.internal/verify?token=" + token;
// Mã hóa URL-safe — không có ký tự + hoặc /
String token = Base64.getUrlEncoder()
    .withoutPadding()
    .encodeToString(sessionData);
String url = "https://auth.internal/verify?token=" + token;
Giải mã với biến thể encoder sai

Vấn đề: Mã hóa với getUrlEncoder() và giải mã với getDecoder() (hoặc ngược lại) ném IllegalArgumentException vì - và _ không hợp lệ trong bảng chữ cái Base64 chuẩn, và + và / không hợp lệ trong bảng chữ cái URL-safe.

Giải pháp: Luôn giải mã với decoder tương ứng: getUrlDecoder() cho URL-safe, getDecoder() cho chuẩn.

Before · Java
After · Java
String encoded = Base64.getUrlEncoder()
    .encodeToString(data);
// Sau đó...
byte[] decoded = Base64.getDecoder()  // Decoder SAI
    .decode(encoded);
// IllegalArgumentException nếu encoded chứa - hoặc _
String encoded = Base64.getUrlEncoder()
    .encodeToString(data);
// Sau đó...
byte[] decoded = Base64.getUrlDecoder()  // decoder tương ứng
    .decode(encoded);
Không đóng OutputStream của wrap()

Vấn đề: Encoder streaming buffer tới 2 byte đầu vào trong khi chờ nhóm 3 byte hoàn chỉnh. Nếu bạn không đóng OutputStream wrapping, 1-4 ký tự Base64 cuối (kể cả padding) không bao giờ được ghi.

Giải pháp: Dùng try-with-resources, hoặc gọi close() tường minh trên stream wrapping trước khi đọc đầu ra.

Before · Java
After · Java
ByteArrayOutputStream baos = new ByteArrayOutputStream();
OutputStream b64os = Base64.getEncoder().wrap(baos);
b64os.write(data);
// baos.toString() bị KHÔNG HOÀN CHỈNH — thiếu các byte cuối
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (OutputStream b64os = Base64.getEncoder().wrap(baos)) {
    b64os.write(data);
}  // close() flush padding cuối cùng
String encoded = baos.toString();  // hoàn chỉnh

Các Phương Thức Mã Hóa Base64 — So Sánh

Phương thức
URL-Safe
Streaming
Ngắt dòng
Kiểu tùy chỉnh
Cần cài đặt
Base64.getEncoder()
✓ (wrap)
Không (JDK 8+)
Base64.getUrlEncoder()
✓ (wrap)
Không (JDK 8+)
Base64.getMimeEncoder()
✓ (wrap)
✓ (76 ký tự)
Không (JDK 8+)
Apache Commons Codec
Phụ thuộc Maven
Guava BaseEncoding
✓ (có thể tùy chỉnh)
Phụ thuộc Maven
jcmd / CLI base64
✓ (pipe)
N/A
Cài đặt hệ thống

Với hầu hết project: java.util.Base64 là lựa chọn đúng đắn. Không có dependency, tích hợp sẵn trong JDK, thread-safe, và bao gồm cả ba biến thể RFC 4648. Chỉ dùng Apache Commons Codec nếu nó đã có trong classpath của bạn và bạn cần phương thức xác thực isBase64() hoặc streaming Base64OutputStream.BaseEncoding của Guava là lựa chọn hợp lý nếu project của bạn đã phụ thuộc vào Guava, nhưng thêm dependency 3 MB chỉ cho Base64 là khó biện minh.

Ba tình huống, ba lựa chọn: web service chuẩn cần mã hóa Basic Auth hoặc JWT? Dùng JDK. Project legacy đang dùng Commons Codec qua Spring hoặc Apache HTTP Client? Dùng nó — không có lý do gì để có hai thư viện Base64 trong classpath. Project dùng Guava cho caching và collection? Dùng BaseEncoding với API fluent rõ ràng. Đừng bao giờ thêm thư viện chỉ cho mã hóa Base64 — phiên bản JDK đã đủ tốt từ năm 2014.

Nếu bạn cần nhanh chóng xác minh kết quả mã hóa mà không cần chạy code Java, hãy dán vào Base64 Encoder để xác nhận đầu ra khớp với những gì code của bạn tạo ra.

Câu Hỏi Thường Gặp

Làm thế nào để mã hóa Base64 một String trong Java?

Trước tiên hãy chuyển đổi chuỗi thành bytes bằng getBytes(StandardCharsets.UTF_8), sau đó truyền mảng byte vào Base64.getEncoder().encodeToString(). Luôn chỉ định UTF-8 tường minh — gọi getBytes() không có đối số charset sẽ dùng giá trị mặc định của nền tảng, vốn khác nhau giữa các hệ điều hành và cấu hình JVM.

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

String payload = "grant_type=client_credentials&scope=read:metrics";
String encoded = Base64.getEncoder()
    .encodeToString(payload.getBytes(StandardCharsets.UTF_8));
// Z3JhbnRfdHlwZT1jbGllbnRfY3JlZGVudGlhbHMmc2NvcGU9cmVhZDptZXRyaWNz

Sự khác biệt giữa Base64.getEncoder() và Base64.getUrlEncoder() là gì?

Cả hai đều mã hóa sang Base64, nhưng getUrlEncoder() sử dụng bảng chữ cái URL-safe theo RFC 4648 Section 5. Nó thay + bằng - và / bằng _ để đầu ra có thể xuất hiện trong URL và tên tệp mà không cần percent-encoding. Encoder chuẩn dùng + và / vốn xung đột với tham số truy vấn URL và các đoạn đường dẫn.

Java
byte[] data = "subject=usr_7b3c&role=admin".getBytes(StandardCharsets.UTF_8);

String standard = Base64.getEncoder().encodeToString(data);
// c3ViamVjdD11c3JfN2IzYyZyb2xlPWFkbWlu

String urlSafe = Base64.getUrlEncoder().encodeToString(data);
// c3ViamVjdD11c3JfN2IzYyZyb2xlPWFkbWlu
// (giống nhau ở đây, nhưng + → - và / → _ khi các ký tự đó xuất hiện)

java.util.Base64 có giống nhau trên Java 8 và Java 17 không?

Có. API java.util.Base64 không thay đổi kể từ khi được giới thiệu trong Java 8. Class này, các class lồng nhau Encoder và Decoder, và tất cả factory method (getEncoder, getUrlEncoder, getMimeEncoder) đều giống hệt nhau trên Java 8, 11, 17 và 21. Không cần di chuyển hay thay đổi code khi nâng cấp phiên bản JDK.

Java
// Code này biên dịch và chạy giống hệt nhau trên Java 8 đến Java 21+
import java.util.Base64;
import java.nio.charset.StandardCharsets;

String encoded = Base64.getEncoder()
    .encodeToString("stable-api".getBytes(StandardCharsets.UTF_8));
System.out.println(encoded);  // c3RhYmxlLWFwaQ==

Làm thế nào để mã hóa Base64 một tệp trong Java?

Đọc tệp vào mảng byte với Files.readAllBytes(Path) và truyền vào Base64.getEncoder().encodeToString(). Với các tệp lớn không nên tải toàn bộ vào bộ nhớ, hãy dùng Base64.getEncoder().wrap(OutputStream) để truyền dữ liệu đầu ra đã mã hóa theo luồng.

Java
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;

byte[] fileBytes = Files.readAllBytes(Path.of("config/tls-cert.pem"));
String encoded = Base64.getEncoder().encodeToString(fileBytes);

Tại sao sun.misc.BASE64Encoder bị deprecated?

sun.misc.BASE64Encoder là class nội bộ của JDK, không bao giờ là một phần của API công khai. Nó nằm trong package sun.misc mà Oracle đã cảnh báo rõ ràng không nên dùng. Java 8 đã giới thiệu java.util.Base64 như là bản thay thế chính thức, công khai và được hỗ trợ. Kể từ Java 9 với hệ thống module, việc truy cập các class sun.misc sẽ tạo ra cảnh báo hoặc lỗi tùy thuộc vào cấu hình JDK.

Java
// Cách cũ — KHÔNG dùng, đã bị xóa trong các JDK hiện đại
// import sun.misc.BASE64Encoder;
// String encoded = new BASE64Encoder().encode(data);

// Cách đúng từ Java 8
import java.util.Base64;
String encoded = Base64.getEncoder().encodeToString(data);

Làm thế nào để thực hiện round-trip mã hóa và giải mã Base64 trong Java?

Mã hóa với Base64.getEncoder().encodeToString(bytes) và giải mã với Base64.getDecoder().decode(encodedString). Chuyển mảng byte đã giải mã trở lại String bằng new String(bytes, StandardCharsets.UTF_8). Round-trip giữ nguyên dữ liệu gốc — miễn là bạn dùng cùng charset cho cả getBytes() và new String().

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

// Mã hóa
String original = "session_token=eyJhbGciOiJSUzI1NiJ9";
byte[] originalBytes = original.getBytes(StandardCharsets.UTF_8);
String encoded = Base64.getEncoder().encodeToString(originalBytes);

// Giải mã
byte[] decodedBytes = Base64.getDecoder().decode(encoded);
String decoded = new String(decodedBytes, StandardCharsets.UTF_8);

System.out.println(original.equals(decoded));  // true

Công Cụ Liên Quan

  • Base64 DecoderGiải mã chuỗi Base64 về dạng văn bản hoặc nhị phân ban đầu — thao tác ngược của mã hóa.
  • URL EncoderPercent-encode chuỗi để dùng an toàn trong URL — khác với mã hóa Base64 URL-safe nhưng thường được dùng cùng nhau.
  • JWT DecoderKiểm tra JWT token có các đoạn header và payload được mã hóa Base64url dạng JSON — giải mã mà không cần thư viện.
  • JSON FormatterĐịnh dạng đẹp các payload JSON trước hoặc sau khi mã hóa Base64 — hữu ích khi debug tích hợp API.
Cũng có sẵn trong:JavaScriptPython
AO
Aisha OseiJava Security & API Engineer

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.

PN
Pavel NovakNgười đánh giá kỹ thuật

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.