Encoder et décoder une chaîne
java.util.Base64 travaille sur des octets. Convertissez les chaînes avec un jeu de caractères explicite : sans argument, getBytes() utilise celui de la plateforme, qui n’était pas toujours UTF-8 avant 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
Les instances de Base64.Encoder et Base64.Decoder sont thread-safe : vous pouvez les garder dans un champ static final.
Base64 URL-safe avec et sans remplissage
getUrlEncoder() utilise - et _. Le remplissage est activé par défaut ; withoutPadding() renvoie un nouvel encodeur qui l’omet :
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);
// <<???>>
Les décodeurs acceptent une entrée avec ou sans remplissage : getDecoder().decode("aGk") fonctionne aussi et renvoie hi. Les segments d’un JWT sont en Base64URL sans remplissage ; voir Base64 ou Base64URL pour savoir quand utiliser l’un ou l’autre.
Encodeur et décodeur MIME
getMimeEncoder() écrit des lignes de 76 caractères séparées par \r\n, comme l’exige la RFC 2045. Il n’ajoute pas de saut de ligne après la dernière ligne.
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 longueur de ligne est arrondie au multiple de 4 inférieur. getMimeDecoder() est le décodeur tolérant : il ignore les séparateurs de ligne et tout autre caractère hors de l’alphabet.
Base64.getMimeDecoder().decode("aGVs\r\nbG8K!"); // bytes of "hello\n"
L’encodeur Base64 MIME produit le même format dans le navigateur.
Encoder et décoder un fichier
Petits fichiers :
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"));
Gros fichiers en flux avec wrap()
Encoder.wrap(OutputStream) et Decoder.wrap(InputStream) encodent et décodent à la volée : la consommation mémoire reste constante, quelle que soit la taille du fichier.
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);
}
Fermer le flux enveloppant est indispensable : l’encodeur écrit le dernier groupe et le remplissage au moment du close(). Si vous l’oubliez, il manque tout le dernier groupe, soit jusqu’à quatre caractères, remplissage compris. Sur un fichier de test de 300 Ko, la sortie correspondait exactement à encodeToString et l’aller-retour était identique à l’octet près. InputStream.transferTo demande Java 9 ; en Java 8, copiez avec une boucle et un tampon.
Validation et exceptions
Les décodeurs basic et URL sont stricts et lèvent java.lang.IllegalArgumentException. Le message indique ce qui ne va pas, avec les caractères affichés en hexadécimal :
Entrée de getDecoder() | Message |
|---|---|
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 |
La validation se résume donc à un try/catch :
static boolean isBase64(String s) {
try {
Base64.getDecoder().decode(s);
return true;
} catch (IllegalArgumentException e) {
return false;
}
}
Une lacune : le décodeur accepte les bits de remplissage non nuls, si bien que aGl= se décode en hi sans broncher. Si vous n’acceptez que des entrées canoniques, réencodez et comparez. Le validateur Base64 montre quel caractère pose problème, et les erreurs Base64 courantes expliquent chaque type d’échec.
Pièges courants
- Le jeu de caractères de la plateforme.
"text".getBytes()etnew String(bytes)utilisent le jeu de caractères par défaut. Sous Windows avec Java 17 ou antérieur, c’était en général Windows-1252, ce qui casse le texte non ASCII. Java 18 a fait d’UTF-8 le défaut (JEP 400), mais passerStandardCharsets.UTF_8ne coûte rien. - Le mauvais décodeur pour l’entrée. Une entrée URL-safe dans le décodeur basic donne
Illegal base64 character 5fou2d. Une entrée MIME donneIllegal base64 character aoud. encode()ouencodeToString().encode()renvoie unbyte[]de caractères ASCII. Afficher ce tableau donne quelque chose comme[B@1b6d3586, pas du Base64.- Ne pas fermer le flux enveloppé. Sans
close(), la sortie de l’encodeur est tronquée. - Croire que Base64 cache les données. Ce n’est pas le cas ; voir encodage ou chiffrement.
Notes de version
- Java 8 : arrivée de
java.util.Base64avec les variantes basic, URL et MIME,withoutPadding()etwrap(). - Java 9 :
InputStream.transferTo, utilisé ci-dessus pour copier les flux. - Java 11 :
javax.xml.bind.DatatypeConverter.printBase64Binarydisparaît avec le retrait de JAXB du JDK. - Java 18 : UTF-8 devient le jeu de caractères par défaut.
Kotlin peut utiliser tout ce qui précède ; il a aussi sa propre API multiplateforme, présentée dans Base64 en Kotlin.
FAQ
Que signifie « Illegal base64 character 5f » ?
Le nombre est le caractère fautif en hexadécimal. 5f correspond à _ et 2d à - : vous décodez du Base64 URL-safe avec getDecoder(). Utilisez plutôt getUrlDecoder(). a est un saut de ligne et 20 une espace ; utilisez getMimeDecoder() ou supprimez les espaces.
Faut-il encore utiliser sun.misc.BASE64Encoder ou javax.xml.bind.DatatypeConverter ?
Non. sun.misc.BASE64Encoder est une API interne qui a disparu des JDK récents, et javax.xml.bind a été retiré du JDK dans Java 11. java.util.Base64 remplace les deux.
Le décodeur Java accepte-t-il du Base64 sans remplissage ?
Oui. Base64.getDecoder().decode("aGk") renvoie les octets de hi. Les décodeurs acceptent un remplissage manquant, mais rejettent un remplissage incorrect.
Comment encoder en Base64 sous Android ?
À partir du niveau d’API 26, java.util.Base64 est disponible. Le code plus ancien utilise android.util.Base64.encodeToString(bytes, Base64.NO_WRAP) ; sans NO_WRAP, des sauts de ligne sont ajoutés.