Codificar e decodificar uma string
O java.util.Base64 trabalha com bytes. Converta as strings com um charset explícito, porque o getBytes() sem argumentos usa o padrão da plataforma, que nem sempre era UTF-8 antes do Java 18.
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
As instâncias de Base64.Encoder e Base64.Decoder são thread-safe, então você pode guardá-las num campo static final.
Base64 URL-safe com e sem preenchimento
O getUrlEncoder() usa - e _. O preenchimento vem ligado por padrão; o withoutPadding() retorna um novo codificador que o 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);
// <<???>>
Os decodificadores aceitam entrada com ou sem preenchimento, então getDecoder().decode("aGk") também funciona e retorna hi. Os segmentos de um JWT são Base64URL sem preenchimento; veja Base64 vs Base64URL para saber quando usar cada um.
Codificador e decodificador MIME
O getMimeEncoder() escreve linhas de 76 caracteres separadas por \r\n, como exige a RFC 2045. Ele não acrescenta quebra de linha depois da última linha.
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
O comprimento da linha é arredondado para baixo até um múltiplo de 4. O getMimeDecoder() é o tolerante: ignora separadores de linha e qualquer outro caractere fora do alfabeto.
Base64.getMimeDecoder().decode("aGVs\r\nbG8K!"); // bytes of "hello\n"
O codificador Base64 MIME gera o mesmo formato no navegador.
Codificar e decodificar um arquivo
Arquivos pequenos:
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"));
Streaming de arquivos grandes com wrap()
O Encoder.wrap(OutputStream) e o Decoder.wrap(InputStream) codificam e decodificam em tempo real, então o uso de memória fica estável, seja qual for o tamanho do arquivo:
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);
}
Fechar o stream externo é importante: o codificador escreve o último grupo e o preenchimento no close(). Se esquecer, falta o último grupo de até quatro caracteres, preenchimento incluído. Num arquivo de teste de 300 KB, a saída bateu exatamente com a do encodeToString, e a ida e volta foi idêntica byte a byte. O InputStream.transferTo exige Java 9; no Java 8, copie com um laço e um buffer.
Validação e exceções
Os decodificadores básico e URL são rígidos e lançam java.lang.IllegalArgumentException. A mensagem diz o que deu errado, com os caracteres impressos em hex:
Entrada para getDecoder() | Mensagem |
|---|---|
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 |
a | Input byte[] should at least have 2 bytes for base64 bytes |
Então validar é só um try/catch:
static boolean isBase64(String s) {
try {
Base64.getDecoder().decode(s);
return true;
} catch (IllegalArgumentException e) {
return false;
}
}
Uma lacuna: o decodificador aceita bits de preenchimento diferentes de zero, então aGl= decodifica para hi sem reclamar. Se você só aceita entrada canônica, recodifique e compare. O validador de Base64 mostra qual caractere é o problema, e erros comuns de Base64 explica cada tipo de falha.
Armadilhas comuns
- Charset da plataforma.
"text".getBytes()enew String(bytes)usam o charset padrão. No Windows com Java 17 ou anterior, ele costumava ser Windows-1252, o que quebra texto não ASCII. O Java 18 tornou o UTF-8 o padrão (JEP 400), mas passarStandardCharsets.UTF_8não custa nada. - Decodificador errado para a entrada. Entrada URL-safe no decodificador básico dá
Illegal base64 character 5fou2d. Entrada MIME dáIllegal base64 character aoud. encode()vsencodeToString(). Oencode()retorna umbyte[]de caracteres ASCII. Imprimir esse array mostra algo como[B@1b6d3586, não Base64.- Não fechar o stream envolvido. Sem o
close(), a saída do codificador fica truncada. - Achar que Base64 esconde dados. Não esconde; veja codificação vs criptografia.
Notas de versão
- Java 8: introdução do
java.util.Base64com as variantes básica, URL e MIME,withoutPadding()ewrap(). - Java 9:
InputStream.transferTo, usado acima para copiar streams. - Java 11:
javax.xml.bind.DatatypeConverter.printBase64Binarydesaparece com a remoção do JAXB do JDK. - Java 18: o UTF-8 passa a ser o charset padrão.
O Kotlin pode usar tudo isso; ele também tem a própria API multiplataforma, abordada em Base64 em Kotlin.
Perguntas frequentes
O que significa 'Illegal base64 character 5f'?
O número é o caractere problemático em hex. 5f é _ e 2d é -, ou seja, você está decodificando Base64 URL-safe com getDecoder(). Use getUrlDecoder(). a é uma quebra de linha e 20 um espaço; use getMimeDecoder() ou remova os espaços em branco.
Ainda devo usar sun.misc.BASE64Encoder ou javax.xml.bind.DatatypeConverter?
Não. sun.misc.BASE64Encoder é uma API interna que sumiu dos JDKs modernos, e o javax.xml.bind foi removido do JDK no Java 11. O java.util.Base64 substitui os dois.
O decodificador do Java aceita Base64 sem preenchimento?
Sim. Base64.getDecoder().decode("aGk") retorna os bytes de hi. Os decodificadores aceitam preenchimento ausente, mas rejeitam preenchimento errado.
Como codificar Base64 no Android?
A partir do API level 26, o java.util.Base64 está disponível. Código mais antigo usa android.util.Base64.encodeToString(bytes, Base64.NO_WRAP); sem o NO_WRAP, ele acrescenta quebras de linha.