Subir un archivo
  1. base64hub
  2. Base64 en código
  3. Java
Código

Base64 en Java

Desde Java 8, java.util.Base64 se encarga de Base64 estándar, URL-safe y MIME, incluido el trabajo en flujo. Todas las salidas de esta página son de Java 21.

Codificar y decodificar una cadena

java.util.Base64 trabaja con bytes. Convierte las cadenas indicando un juego de caracteres explícito, porque getBytes() sin argumentos usa el predeterminado de la plataforma, que antes de Java 18 no siempre era UTF-8.

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

String enc = Base64.getEncoder()
        .encodeToString("héllo wörld".getBytes(StandardCharsets.UTF_8));
System.out.println(enc);   // aMOpbGxvIHfDtnJsZA==

String dec = new String(Base64.getDecoder().decode(enc), StandardCharsets.UTF_8);
System.out.println(dec);   // héllo wörld

Las instancias de Base64.Encoder y Base64.Decoder son thread-safe, así que puedes guardarlas en un campo static final.

Base64 URL-safe con y sin relleno

getUrlEncoder() usa - y _. El relleno está activado por defecto; withoutPadding() devuelve un codificador nuevo que lo omite:

byte[] d = "<<???>>".getBytes(StandardCharsets.UTF_8);

Base64.getUrlEncoder().encodeToString(d);                   // PDw_Pz8-Pg==
Base64.getUrlEncoder().withoutPadding().encodeToString(d);  // PDw_Pz8-Pg

new String(Base64.getUrlDecoder().decode("PDw_Pz8-Pg"), StandardCharsets.UTF_8);
// <<???>>

Los decodificadores aceptan entradas con o sin relleno, así que getDecoder().decode("aGk") también funciona y devuelve hi. Los segmentos de un JWT son Base64URL sin relleno; consulta Base64 frente a Base64URL para saber cuándo usar cada uno.

Codificador y decodificador MIME

getMimeEncoder() escribe líneas de 76 caracteres separadas por \r\n, como exige el RFC 2045. No añade un salto de línea después de la última línea.

String mime = Base64.getMimeEncoder().encodeToString("x".repeat(60).getBytes());
// eHh4eHh4...eHh4  (76 chars) + "\r\n" + "eHh4"

Base64.getMimeEncoder(64, "\n".getBytes());   // PEM-style 64-char lines

La longitud de línea se redondea hacia abajo a un múltiplo de 4. getMimeDecoder() es el tolerante: ignora los separadores de línea y cualquier otro carácter fuera del alfabeto.

Base64.getMimeDecoder().decode("aGVs\r\nbG8K!");   // bytes of "hello\n"

El codificador Base64 MIME genera el mismo formato en el navegador.

Codificar y decodificar un archivo

Archivos pequeños:

import java.nio.file.*;

byte[] file = Files.readAllBytes(Path.of("hello.txt"));
System.out.println(Base64.getEncoder().encodeToString(file));   // aGVsbG8K

Files.write(Path.of("copy.txt"), Base64.getDecoder().decode("aGVsbG8K"));

Archivos grandes en flujo con wrap()

Encoder.wrap(OutputStream) y Decoder.wrap(InputStream) codifican y decodifican sobre la marcha, así que el uso de memoria se mantiene estable sea cual sea el tamaño del archivo:

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

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

Cerrar el stream envolvente es importante: el codificador escribe el último grupo y el relleno en close(). Si lo olvidas, falta el último grupo de hasta cuatro caracteres, relleno incluido. Con un archivo de prueba de 300 KB, la salida coincidió exactamente con encodeToString y la ida y vuelta fue idéntica byte a byte. InputStream.transferTo necesita Java 9; en Java 8, copia con un bucle y un búfer.

Validación y excepciones

Los decodificadores básico y URL son estrictos y lanzan java.lang.IllegalArgumentException. El mensaje dice qué ha fallado y muestra los caracteres en hexadecimal:

Entrada a getDecoder()Mensaje
PDw_Pz8-Pg==Illegal base64 character 5f
"aGVs\nbG8K"Illegal base64 character a
aGk=!!Input byte array has incorrect ending byte at 4
aGk==Input byte array has incorrect ending byte at 4
aInput byte[] should at least have 2 bytes for base64 bytes

Así que validar es solo un try/catch:

static boolean isBase64(String s) {
    try {
        Base64.getDecoder().decode(s);
        return true;
    } catch (IllegalArgumentException e) {
        return false;
    }
}

Un hueco: el decodificador acepta bits de relleno distintos de cero, así que aGl= se decodifica como hi sin quejarse. Si solo quieres entradas canónicas, vuelve a codificar y compara. El validador Base64 muestra qué carácter es el problema, y errores habituales de Base64 explica cada tipo de fallo.

Errores habituales

  • El juego de caracteres de la plataforma. "text".getBytes() y new String(bytes) usan el juego de caracteres predeterminado. En Windows con Java 17 o anterior solía ser Windows-1252, lo que estropea el texto no ASCII. Java 18 convirtió UTF-8 en el predeterminado (JEP 400), pero pasar StandardCharsets.UTF_8 no cuesta nada.
  • Un decodificador que no corresponde a la entrada. Una entrada URL-safe en el decodificador básico da Illegal base64 character 5f o 2d. Una entrada MIME da Illegal base64 character a o d.
  • encode() frente a encodeToString(). encode() devuelve un byte[] de caracteres ASCII. Si imprimes ese array verás algo como [B@1b6d3586, no Base64.
  • No cerrar el stream envuelto. Sin close(), la salida del codificador queda truncada.
  • Creer que Base64 oculta los datos. No los oculta; consulta codificación frente a cifrado.

Notas de versión

  • Java 8: se introduce java.util.Base64 con las variantes básica, URL y MIME, withoutPadding() y wrap().
  • Java 9: InputStream.transferTo, usado arriba para copiar streams.
  • Java 11: javax.xml.bind.DatatypeConverter.printBase64Binary desaparece al eliminarse JAXB del JDK.
  • Java 18: UTF-8 pasa a ser el juego de caracteres predeterminado.

Kotlin puede usar todo lo anterior; además tiene su propia API multiplataforma, que se explica en Base64 en Kotlin.

Preguntas frecuentes

¿Qué significa «Illegal base64 character 5f»?

El número es el carácter problemático en hexadecimal. 5f es _ y 2d es -, lo que significa que estás decodificando Base64 URL-safe con getDecoder(). Usa getUrlDecoder() en su lugar. a es un salto de línea y 20 un espacio; usa getMimeDecoder() o quita los espacios en blanco.

¿Debo seguir usando sun.misc.BASE64Encoder o javax.xml.bind.DatatypeConverter?

No. sun.misc.BASE64Encoder es una API interna que ya no existe en los JDK modernos, y javax.xml.bind se eliminó del JDK en Java 11. java.util.Base64 sustituye a ambas.

¿El decodificador de Java acepta Base64 sin relleno?

Sí. Base64.getDecoder().decode("aGk") devuelve los bytes de hi. Los decodificadores aceptan que falte el relleno, pero rechazan un relleno incorrecto.

¿Cómo codifico en Base64 en Android?

A partir del nivel de API 26 está disponible java.util.Base64. El código antiguo usa android.util.Base64.encodeToString(bytes, Base64.NO_WRAP); sin NO_WRAP, añade saltos de línea.

Sigue leyendo