Enviar um arquivo
  1. base64hub
  2. Base64 no código
  3. Java
Código

Base64 em Java

Desde o Java 8, o java.util.Base64 cuida do Base64 padrão, URL-safe e MIME, inclusive em streaming. Todas as saídas abaixo vêm do Java 21.

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
aInput 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() e new 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 passar StandardCharsets.UTF_8 não custa nada.
  • Decodificador errado para a entrada. Entrada URL-safe no decodificador básico dá Illegal base64 character 5f ou 2d. Entrada MIME dá Illegal base64 character a ou d.
  • encode() vs encodeToString(). O encode() retorna um byte[] 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.Base64 com as variantes básica, URL e MIME, withoutPadding() e wrap().
  • Java 9: InputStream.transferTo, usado acima para copiar streams.
  • Java 11: javax.xml.bind.DatatypeConverter.printBase64Binary desaparece 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.

Continue lendo