Base64 Encode/Decode
Base64 is a way to represent binary data — images, files, tokens, encrypted bytes — as plain ASCII text so it travels safely through channels that only handle text (HTTP headers, JSON fields, email bodies). Java 8 introduced java.util.Base64, a clean, built-in API that replaces older workarounds and covers every common encoding flavor.
Why Base64?
Binary bytes such as 0xFF or 0x00 can get corrupted or misread when pasted into a URL or an email. Base64 maps every 3 bytes of input into 4 printable characters from a 64-character alphabet (A-Z, a-z, 0-9, +, /). The result is about 33 % larger than the original, but it is completely safe to store and transmit as text.
Note: Base64 is encoding, not encryption. Anyone can decode it instantly. Never rely on it for security.
The Three Flavors
Base64 exposes three nested encoder/decoder pairs:
| Flavor | Factory method | Alphabet differs? | Line breaks? | Typical use |
|---|---|---|---|---|
| Basic | Base64.getEncoder() | No (+, /) | No | General binary-to-text |
| URL & Filename safe | Base64.getUrlEncoder() | Yes (-, _) | No | URLs, file names, JWTs |
| MIME | Base64.getMimeEncoder() | No | Yes (76 chars + CRLF) | Email attachments |
Basic Encoding and Decoding
import java.util.Base64;
public class BasicBase64 {
public static void main(String[] args) {
String original = "Hello, Java 8!";
// Encode
String encoded = Base64.getEncoder()
.encodeToString(original.getBytes());
System.out.println("Encoded : " + encoded);
// Decode
byte[] decodedBytes = Base64.getDecoder().decode(encoded);
String decoded = new String(decodedBytes);
System.out.println("Decoded : " + decoded);
}
}
Output:
Encoded : SGVsbG8sIEphdmEgOCE=
Decoded : Hello, Java 8!
The trailing = character is padding — Base64 works in groups of 3 bytes, so it pads the last group to reach a multiple of 4 output characters.
Encoding Without Padding
If your target system rejects = padding (some JWT libraries do), strip it:
String noPad = Base64.getEncoder()
.withoutPadding()
.encodeToString("Java".getBytes());
System.out.println(noPad); // SmF2YQ (no trailing =)
URL-Safe Encoding
The standard + and / characters are special in URLs. The URL-safe encoder replaces them with - and _:
import java.util.Base64;
public class UrlSafeBase64 {
public static void main(String[] args) {
// bytes that produce + or / in standard Base64
byte[] data = {(byte) 0xFB, (byte) 0xFF, (byte) 0xFE};
String standard = Base64.getEncoder().encodeToString(data);
String urlSafe = Base64.getUrlEncoder().encodeToString(data);
System.out.println("Standard : " + standard); // +//+
System.out.println("URL-safe : " + urlSafe); // -__-
}
}
Output:
Standard : +//+
URL-safe : -__-
Tip: Always use URL-safe encoding when the Base64 string will appear inside a URL query parameter or path segment, or when building JWTs.
MIME Encoding (For Email / Large Payloads)
MIME Base64 inserts a CRLF (\r\n) line break every 76 characters, which is required by the email standard (RFC 2045):
import java.util.Base64;
public class MimeBase64 {
public static void main(String[] args) {
byte[] data = new byte[100]; // 100 zero bytes as a stand-in
for (int i = 0; i < data.length; i++) data[i] = (byte) i;
String mime = Base64.getMimeEncoder().encodeToString(data);
System.out.println(mime);
}
}
Output:
AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v
MDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk8=
You can also customize the line length and separator:
Base64.Encoder customMime = Base64.getMimeEncoder(60, new byte[]{'\n'});
Encoding Files and Byte Arrays
You are not limited to strings. Any byte[] can be encoded — images, PDFs, encryption keys, anything:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
public class FileBase64 {
public static void main(String[] args) throws IOException {
// Read any binary file
byte[] fileBytes = Files.readAllBytes(Path.of("logo.png"));
// Encode to a Base64 string (e.g. for embedding in HTML/CSS)
String b64 = Base64.getEncoder().encodeToString(fileBytes);
System.out.println("First 60 chars: " + b64.substring(0, 60));
// Decode back to bytes and write
byte[] restored = Base64.getDecoder().decode(b64);
Files.write(Path.of("logo_restored.png"), restored);
}
}
Warning: Embedding large files as Base64 inside JSON or HTML inflates their size by ~33 %. For assets over a few KB, prefer serving them separately.
Wrapping Streams
For very large data, Base64 can wrap InputStream / OutputStream so you never hold the whole encoded payload in memory:
import java.io.*;
import java.util.Base64;
public class StreamBase64 {
public static void main(String[] args) throws IOException {
// Encoding to a stream
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (OutputStream enc = Base64.getEncoder().wrap(out)) {
enc.write("Streaming Base64 in Java!".getBytes());
}
System.out.println(out.toString()); // SGVsbG8gU3RyZWFtIQ==...
// Decoding from a stream
byte[] encoded = out.toByteArray();
ByteArrayInputStream in = new ByteArrayInputStream(encoded);
try (InputStream dec = Base64.getDecoder().wrap(in)) {
System.out.println(new String(dec.readAllBytes()));
}
}
}
This is ideal when piping large files through an encoding layer without allocating a single giant buffer. See Java I/O for more on working with streams.
Under the Hood
Alphabet mapping. The encoder splits every 3-byte group into four 6-bit indices (3 × 8 = 24 bits = 4 × 6) and looks each index up in a 64-character table. The decoder does the inverse lookup. The JDK implementation uses a single pre-computed lookup table for each direction — the entire encode/decode cycle is a tight loop of bit-shift and array-index operations with no heap allocation per character.
Padding math. If the input length is not a multiple of 3, the last group is:
- 1 remaining byte → 2 Base64 chars +
== - 2 remaining bytes → 3 Base64 chars +
=
withoutPadding() is zero-cost. It simply skips writing those final = characters; decoding still works because the length of the encoded string implies how many padding bytes existed.
Thread safety. Base64.Encoder and Base64.Decoder instances are stateless and thread-safe — you can safely store them in a static final field and reuse them across threads without synchronization overhead.
Older alternatives. Before Java 8, developers used sun.misc.BASE64Encoder (an internal, unsupported class) or third-party libraries like Apache Commons Codec. The java.util.Base64 API is the definitive replacement — faster, fully supported, and available on all JVMs.
Quick Reference
// Encoder instances (reusable, thread-safe)
Base64.Encoder basic = Base64.getEncoder();
Base64.Encoder url = Base64.getUrlEncoder();
Base64.Encoder mime = Base64.getMimeEncoder();
Base64.Encoder noPad = Base64.getEncoder().withoutPadding();
// Decoder instances
Base64.Decoder basicDec = Base64.getDecoder();
Base64.Decoder urlDec = Base64.getUrlDecoder();
Base64.Decoder mimeDec = Base64.getMimeDecoder();
// Encode a String
String enc = basic.encodeToString("hello".getBytes());
// Decode to a String
String dec = new String(basicDec.decode(enc));
Related Topics
- Java 8 Features — overview of all Java 8 additions including streams, lambdas, and the new date/time API.
- Java I/O — byte and character streams that Base64 wraps when processing large files.
- String Methods — utilities for manipulating the encoded string output.
- Serialization — a common scenario where serialized byte arrays are Base64-encoded for transport.
- HttpURLConnection — where Basic Auth credentials are sent as Base64 in the
Authorizationheader.