Codificación y Decodificación en Base 64 en Java

Base64 convierte bytes en texto imprimible para poder meterlos donde solo caben caracteres: una URL, una cabecera HTTP, un correo, un JSON. Desde Java 8 está en el JDK y no hace falta ninguna biblioteca.

Ida y vuelta

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

void main() {
    String original = "decodigo.com: año 2026 ñ";

    String cod = Base64.getEncoder().encodeToString(original.getBytes(StandardCharsets.UTF_8));
    IO.println("codificado  : " + cod);

    byte[] bytes = Base64.getDecoder().decode(cod);
    IO.println("decodificado: " + new String(bytes, StandardCharsets.UTF_8));
}
salidacodificado  : ZGVjb2RpZ28uY29tOiBhw7FvIDIwMjYgw7E=
decodificado: decodigo.com: año 2026 ñ
Base64 trabaja con bytes, no con texto. Por eso hay que decir en qué codificación se convierte la cadena a bytes, y usar la misma al volver. Si codificas en UTF-8 y decodificas en ISO-8859-1, las eñes se rompen. Pon StandardCharsets.UTF_8 en los dos lados y olvídate.

La variante para URL

String texto = "subcarpeta/archivo?x=1";

IO.println("estándar: " + Base64.getEncoder().encodeToString(texto.getBytes(UTF_8)));
IO.println("para URL: " + Base64.getUrlEncoder().withoutPadding()
                                .encodeToString(texto.getBytes(UTF_8)));
salidaestándar: c3ViY2FycGV0YS9hcmNoaXZvP3g9MQ==
para URL: c3ViY2FycGV0YS9hcmNoaXZvP3g9MQ

El alfabeto normal usa + y /, que en una URL significan otra cosa y hay que volver a escapar. getUrlEncoder los cambia por - y _. El = del final es relleno; withoutPadding() lo quita, y el decodificador lo acepta igual. Para decodificar hay que usar getUrlDecoder: los alfabetos no son intercambiables.

Hay un tercero, getMimeEncoder(), que parte la salida en líneas de 76 caracteres porque así lo pide el correo electrónico.

Un archivo

String cod = Base64.getEncoder().encodeToString(Files.readAllBytes(Path.of("foto.png")));
Files.write(Path.of("copia.png"), Base64.getDecoder().decode(cod));

Es lo que hay detrás de las imágenes incrustadas en HTML con data:image/png;base64,… y de los adjuntos de un correo. Para archivos grandes conviene Base64.getEncoder().wrap(flujo), que trabaja por partes y no carga todo en memoria.

Dos cosas que hay que tener claras

IO.println("original  : " + original.getBytes(UTF_8).length + " bytes");
IO.println("codificado: " + cod.length() + " caracteres");
salidaoriginal  : 26 bytes
codificado: 36 caracteres

Base64 engorda. Cada 3 bytes se convierten en 4 caracteres, así que el resultado ocupa un tercio más. Mandar una imagen en Base64 dentro de un JSON cuesta un 33 % más de red que mandarla como archivo.

Y Base64 no es cifrado. No tiene clave, no oculta nada: cualquiera lo revierte en un segundo. Se usa para transportar datos binarios por un canal de texto, no para protegerlos. Una contraseña en Base64 en un archivo de configuración está, a efectos prácticos, en claro.

Cuando lo que llega no es Base64

Base64.getDecoder().decode("esto no es base64 válido!!");
salidaIllegalArgumentException: Illegal base64 character 20

El 20 es hexadecimal: el espacio. El decodificador estricto rechaza cualquier carácter fuera del alfabeto, saltos de línea incluidos; si lo que recibes puede venir partido en líneas, usa getMimeDecoder(), que los ignora.

Más información en inglés: Base64 encoding and decoding in Java.

Todo el código se compiló y se ejecutó con Java 25 LTS en un contenedor limpio antes de publicar esta página; las salidas están copiadas de esa ejecución.