Encodage Base64 en Dart : un guide complet
Vous avez des octets, et il vous faut une chaîne. Le payload peut être un fichier, un identifiant d'authentification, un jeton de configuration, ou un bloc binaire embarqué dans un document JSON, et le canal n'accepte que du texte. Le Base64 est l'échange qui règle ça : chaque trio d'octets d'entrée devient quatre caractères tirés d'un alphabet de 64 caractères, si bien que la sortie est toujours un multiple propre de quatre et toujours sûre dans les mondes texte uniquement. Le prix est fixe, 33 pour cent de caractères en plus, et le format ajoute un ou deux caractères de padding = à la fin quand le dernier bloc est court. Ce guide est la recette Dart pour faire cet échange correctement.
Rien à installer. Le Base64 est livré dans dart:convert depuis Dart 1.13 en 2015, et l'API est stable depuis ; les deux alphabets, standard et URL-safe, sont disponibles depuis plus de dix ans. La page d'accueil détaille le format en profondeur ; voici le côté encodage du travail : la surface API complète, la discipline octets d'abord qui évite le bug le plus courant, les choix de padding et d'alphabet, et les tâches du monde réel : JWT, data URIs, envois de fichiers, en-têtes HTTP, MIME, configuration, streams et ligne de commande. Le décodage, la direction inverse, a son propre guide, lié à la fin.
Un import, deux alphabets, une règle de padding
Toute la surface publique de l'encodage vit dans dart:convert :
| Entrée | Alphabet | Quand y recourir |
|---|---|---|
base64Encode(bytes) |
standard : A-Z a-z 0-9 + /, avec padding |
API, MIME, authentification Basic, la plupart des consommateurs |
base64UrlEncode(bytes) |
URL-safe : A-Z a-z 0-9 - _, toujours avec padding |
URL, noms de fichiers, JWT, identifiants d'objets |
base64.encode(bytes) |
standard, identique à l'appel de premier niveau | Transformations de stream et pipelines de codec |
Base64Encoder().convert(bytes) |
standard | Vous voulez une instance d'encodeur nommée |
Deux règles couvrent les quatre lignes. D'abord, l'entrée doit être une liste de valeurs d'octets, des entiers de 0 à 255 ; tout le reste, y compris les négatifs, ou 256 et au-dessus, lève une ArgumentError qui nomme l'index fautif. Ensuite, la sortie est toujours avec padding : il n'y a pas d'option, de constructeur ou de réglage qui produise une sortie sans padding, car le padding du format est une propriété des données, et les specs qui veulent le voir disparaître le suppriment dans une étape séparée et documentée. Le plus petit exemple possible, de bout en bout :
import 'dart:convert';
void main() {
final text = 'Dart is open source';
final bytes = utf8.encode(text);
final encoded = base64Encode(bytes);
print(encoded); // RGFydCBpcyBvcGVuIHNvdXJjZQ==
}
Les octets d'abord : l'ordre qui vous sauve
Le bug base64 le plus courant de Dart n'a rien à voir avec le base64. Il est dans l'ordre des opérations. Une String de Dart est une suite d'unités de code UTF-16, et appeler base64Encode(text.codeUnits) emballe ces unités de 16 bits, pas les octets que le récepteur attend. Pour du pur ASCII, les deux coïncident par chance, et c'est pourquoi le bug se cache jusqu'à l'arrivée du premier caractère accentué, emoji ou texte CJK. Alors l'encodeur refuse le travail, car une unité de code comme 0x4e16 n'est pas une valeur d'octet :
import 'dart:convert';
void main() {
final message = 'Héllo Wörld 世界';
print(utf8.encode(message).length); // 20
print(message.codeUnits.length); // 14
print(base64Encode(utf8.encode(message)));
try {
base64Encode(message.codeUnits);
} on ArgumentError catch (e) {
print(e);
}
}
L'ArgumentError pointe l'index fautif exact, si bien que l'échec est bruyant plutôt que silencieux. La discipline à garder : décider ce que sont les octets avant de parler au base64. Le texte passe par un encodage nommé, utf8.encode pour les données modernes, et c'est le List<int> résultant qui est emballé. Les octets d'un fichier ou d'une socket réseau arrivent déjà en Uint8List, ce qui est la bonne forme pour l'encodeur sans aucune conversion.
Le padding : le travail de l'encodeur
Le Base64 associe des groupes de trois octets à quatre caractères, si bien qu'un payload dont la longueur n'est pas un multiple de trois laisse un groupe partiel à la fin. Le format marque ce manque avec des caractères = : un octet d'entrée devient quatre caractères plus deux pads, deux octets deviennent quatre caractères plus un pad, trois octets deviennent exactement quatre caractères. L'encodeur de Dart le fait pour vous, inconditionnellement :
import 'dart:convert';
void main() {
print(base64Encode([0x41])); // QQ==
print(base64Encode([0x41, 0x42])); // QUI=
print(base64Encode([0x41, 0x42, 0x43])); // QUJD
}
Ce comportement inconditionnel est une qualité : la sortie est toujours une chaîne base64 légale et auto-descriptive. Quand une spec demande la variante sans padding, et les JWT en sont la raison habituelle, la suppression est votre étape explicite et visible, pas un réglage de bibliothèque :
base64UrlEncode(bytes).replaceAll('=', '')
Posez la suppression là où est la frontière de la spec, nommez-la, et documentez-la. Le côté décodeur de cet échange, y compris la façon dont une entrée endommagée ou dépouillée est réparée, est couvert dans le guide de décodage.
Le Base64 URL-safe
L'alphabet standard contient +, / et =, et ces trois caractères entrent en collision avec la syntaxe des URL : séparateurs de requête, séparateurs de chemin et délimiteurs de paramètres. L'alphabet URL-safe, standardisé sous le nom base64url dans la RFC 4648, échange + contre - et / contre _, si bien que la sortie peut tenir dans un segment de chemin, une valeur de requête ou un nom de fichier sans échappement. Voici la différence sur des octets qui font travailler les deux caractères échangés :
import 'dart:convert';
void main() {
final tricky = [0xfb, 0xff, 0xfe, 0xf9];
print(base64Encode(tricky)); // +//++Q==
print(base64UrlEncode(tricky)); // -__--Q==
}
Choisissez selon le consommateur, pas selon le goût. Si la valeur va vivre dans une URL, un JWT ou un nom de fichier, encodez avec base64UrlEncode et supprimez le padding si la spec est sans padding. Si la valeur sera un corps MIME, un en-tête d'authentification Basic, ou un champ d'un contrat d'API qui dit « base64 », utilisez l'alphabet standard, car base64 sans précision veut dire le standard. Les deux alphabets ne sont pas interchangeables aux yeux des consommateurs stricts : un serveur qui attend du base64 standard peut refuser un payload contenant - avec un 400 et rien de plus utile.
Les charsets : quels octets emballez-vous ?
Quand l'entrée est du texte, l'étape d'encodage décide quels octets le base64 verra, et le consommateur suppose un charset de l'autre côté. Si votre supposition et celle du consommateur diffèrent, la sortie est un base64 parfaitement valide des mauvais octets, le pire type de bug, car rien ne lève d'exception. Pour tout échange moderne, l'UTF-8 est la valeur par défaut ; les autres encodages sur octet simple existent pour les données héritées :
| Encodage | À utiliser pour | Encoder avec |
|---|---|---|
utf8 |
Texte moderne, JSON, tout ce qui est sur le web | utf8.encode(text) |
latin1 |
Données occidentales héritées sur octet simple | latin1.encode(text) |
ascii |
Texte 7 bits simple | ascii.encode(text) |
import 'dart:convert';
void main() {
final modern = base64Encode(utf8.encode('Héllo'));
final legacy = base64Encode(latin1.encode('Héllo'));
print(modern); // SMOpbGxv
print(legacy); // SOlsbG8=
}
Même mot, octets différents, base64 différent. Notez les longueurs : l'UTF-8 a besoin de six octets pour Héllo car l'accent est une séquence de deux octets, tandis que le Latin-1 le tient en cinq. Si le consommateur décode avec l'encodage que vous n'avez pas utilisé, il obtient du charabia, et cela ressemblera à des données corrompues en transit alors qu'elles l'étaient dans l'intention.
Les JWT : écrire le jeton
Un JSON Web Token est trois parties base64url jointes par des points : en-tête, payload, signature. La RFC 7515 fixe deux détails : l'alphabet est URL-safe, et le padding est omis, car le jeton est conçu pour tenir dans les URL et les en-têtes. La signature de l'algorithme HS256 est le HMAC-SHA256 de header.payload, lui-même en base64url sans padding. Le bricoler avec le paquet crypto tient en quelques lignes, et c'est plus transparent qu'il n'y paraît :
import 'dart:convert';
import 'package:crypto/crypto.dart';
String base64UrlNoPadding(List<int> bytes) {
return base64UrlEncode(bytes).replaceAll('=', '');
}
String createJwt(Map<String, dynamic> header, Map<String, dynamic> payload,
List<int> secretKey) {
final signingInput =
'${base64UrlNoPadding(utf8.encode(jsonEncode(header)))}.'
'${base64UrlNoPadding(utf8.encode(jsonEncode(payload)))}';
final mac = Hmac(sha256, secretKey).convert(utf8.encode(signingInput));
final signature = base64UrlNoPadding(mac.bytes);
return '$signingInput.$signature';
}
void main() {
final token = createJwt(
{'alg': 'HS256', 'typ': 'JWT'},
{'sub': 'user-42', 'exp': 1893456000},
utf8.encode('a-32-byte-secret-key-0123456789'),
);
print(token);
}
La signature est calculée sur les octets exacts qui ont été emballés, si bien que tant que vous signez la même chaîne que vous émettez, la vérification de l'autre côté est un enchaînement des mêmes étapes. Trois avertissements. Le vieux paquet jwt sur pub.dev date de 2014 et précède la null safety ; la réponse qui fonctionne dans l'écosystème est de faire ce qui est montré ici avec crypto. N'émettez jamais un jeton avec alg: none, et ne laissez jamais un client choisir l'algorithme. Et rappelez-vous que le payload est lisible par n'importe qui, n'incluez donc que ce que le jeton doit prouver.
Les data URIs : expédier des fichiers dans du texte
Une data URI, définie par la RFC 2397, est une URL dont le payload est les données elles-mêmes. Le contenu binaire dans une data URI est encodé en base64, et c'est pourquoi le format apparaît partout où des documents texte doivent embarquer des images, des polices ou des pièces jointes : attributs HTML, CSS, JSON, fichiers de configuration. Dart peut construire les URI nativement, sans paquet d'URI :
import 'dart:convert';
import 'dart:io';
Future<void> main() async {
final png = await File('icon.png').readAsBytes();
final imageUri = Uri.dataFromBytes(png, mimeType: 'image/png');
print(imageUri); // data:image/png;base64,iVBOR...
final note = Uri.dataFromString('Hello, Dart!');
print(note); // data:,Hello,%20Dart!
}
Uri.dataFromBytes encode en base64 par défaut (il a une option percentEncoded: true pour l'autre forme), ce qui est l'encodage correct pour du binaire. Uri.dataFromString encode en pourcentage par défaut, car du texte court est plus court ainsi, et accepte un drapeau base64: true quand vous voulez la forme octets emballés. Le piège pratique est l'échelle : le payload voyage à l'intérieur du document, avec 33 pour cent de surcoût, si bien que les data URIs sont destinées à de petites ressources, des icônes et des vignettes, pas à expédier des mégaoctets par CSS.
Les fichiers : emballer des octets pour les canaux texte
Le travail du quotidien : un fichier qui doit voyager par JSON, un fichier de configuration, ou tout transport texte uniquement. Le schéma est lire les octets, encoder, embarquer :
import 'dart:convert';
import 'dart:io';
Future<void> main() async {
final image = await File('photo.jpg').readAsBytes();
final encoded = base64Encode(image);
final upload = jsonEncode({
'name': 'photo.jpg',
'size': image.length,
'data': encoded,
});
print('payload ${upload.length} chars for ${image.length} bytes');
}
Le chiffre à garder en tête est la croissance : un fichier de 2 000 octets devient 2 668 caractères base64, et un peu plus quand les clés JSON s'ajoutent. Deux pièges. D'abord, vérifiez que votre entrée n'est pas déjà encodée : encoder en base64 une chaîne déjà base64 est le bug classique de l'encodage double, et il se décode « avec succès » en un autre mur de base64. Ensuite, si le canal peut porter du binaire, ce à quoi sert multipart/form-data, portez du binaire : c'est un quart plus petit, et l'impôt base64 est du pur gaspillage.
HTTP et API : en-têtes et payloads
Le travail d'encodage le plus connu de HTTP est l'en-tête Authorization: Basic : le mot Basic, un espace, et le base64 d'alphabet standard de username:password :
import 'dart:convert';
import 'package:http/http.dart' as http;
Future<void> main() async {
final credentials = base64Encode(utf8.encode('octocat:secret'));
final client = http.Client();
final response = await client.get(
Uri.parse('https://httpbin.org/basic-auth/octocat/secret'),
headers: {'Authorization': 'Basic $credentials'},
);
print(response.statusCode);
client.close();
}
Avec le paquet http, à un dart pub add http près, l'en-tête n'est qu'une chaîne dans la requête. Le piège est le cadrage sécurité : le base64 ici est un brouillage, pas une protection. N'importe qui peut l'inverser en une étape, et c'est exactement pourquoi l'authentification Basic n'a sa place que sur des connexions TLS, où c'est le transport, pas l'encodage, qui protège. Pour les champs de payload d'API, suivez le contrat : s'il dit base64, c'est l'alphabet standard avec padding, et la variante URL-safe est autre chose que les consommateurs stricts refuseront.
L'email et le MIME : envelopper à 76
Le MIME, le système qui laisse l'email porter du binaire, utilise le base64 comme encodage de transfert de contenu, et la RFC 2045 précise que les lignes encodées ne doivent pas dépasser 76 caractères, avec du CRLF entre elles. La limite est une convention MIME - 76 plus CRLF tient confortablement sur un affichage de 80 colonnes - et chaque encodeur conforme enveloppe. L'encodeur de Dart produit une chaîne ininterrompue, si bien que l'enveloppement est une courte étape de post-traitement :
import 'dart:convert';
String wrapForMime(String base64Text, [int lineLength = 76]) {
final buffer = StringBuffer();
for (var i = 0; i < base64Text.length; i += lineLength) {
final end = i + lineLength > base64Text.length
? base64Text.length
: i + lineLength;
buffer
..write(base64Text.substring(i, end))
..write('\r\n');
}
return buffer.toString();
}
void main() {
final encoded = base64Encode(utf8.encode('Hello from an email attachment'));
print(wrapForMime(encoded));
}
Enveloppez la chaîne terminée, padding compris, et laissez la dernière ligne aussi longue qu'elle est, jusqu'à 76. La seule chose à ne pas faire est de supprimer le padding avant d'envelopper dans l'espoir d'économiser un caractère : les pads font partie du contenu encodé, et un consommateur qui réassemble les lignes refusera le résultat s'ils manquent.
Configuration : des secrets en une ligne
Les jetons, clés et identifiants qui contiennent des guillemets, des retours à la ligne ou d'autres caractères gênants sont parfois encodés en base64 pour tenir proprement sur une ligne de configuration ou une variable CI. Le cadrage honnête d'abord : c'est un brouillage, pas un chiffrement, et tout ce qui atteint un dépôt ou un journal est public. Utilisez le schéma pour la propreté, jamais pour le secret. Encoder la valeur tient en un appel :
import 'dart:convert';
String forEnvFile(String secret) {
return base64Encode(utf8.encode(secret));
}
void main() {
final line = 'API_TOKEN_B64=${forEnvFile('sk-live-abc123')}';
print(line); // API_TOKEN_B64=c2stbGl2ZS1hYmMxMjM=
}
La valeur est alors posée dans un fichier .env, un secret CI ou une définition à la compilation, et revient en texte brut après un seul décodage. Si le secret doit être protégé en transit ou au repos, saisissez un gestionnaire de secrets ou une bibliothèque de chiffrement ; le travail du base64 ici est de garder le traitement texte du pipeline simple, rien de plus.
Les streams : encoder à travers les bords de blocs
Quand les octets arrivent en blocs, une lecture réseau, un fichier traité par blocs, l'encodeur s'en sort sans que vous aligniez quoi que ce soit. Le codec porte le groupe partiel à travers les bords de blocs, si bien que les tailles de blocs n'ont pas besoin d'être des multiples de trois :
import 'dart:convert';
import 'dart:typed_data';
Future<void> main() async {
final data = Uint8List(100000);
for (var i = 0; i < data.length; i += 31) {
data[i] = i % 256;
}
final chunks = <List<int>>[data.sublist(0, 777), data.sublist(777)];
final encoded = await Stream.fromIterable(chunks)
.transform(base64.encoder)
.join();
print('in: ${data.length}, out: ${encoded.length}'); // in: 100000, out: 133336
}
Deux blocs de tailles maladroites, 777 et 99 223 octets, produisent une seule chaîne correcte de 133 336 caractères, car l'encodeur gare les bits restants de chaque groupe incomplet jusqu'à l'arrivée du bloc suivant, et n'émet le padding qu'à la fin. Si vous préférez les sinks, base64.encoder.startChunkedConversion vous donne la même machine à états en tant que ByteConversionSink (vous lui donnez des blocs d'octets, il émet des chaînes), ce qui est l'ajustement naturel pour écrire de grosses sorties dans un fichier ou une socket sans jamais joindre une seule grosse chaîne.
Les grosses données : débit et mémoire
Le calcul des tailles est exact et mérite d'être retenu : la longueur de sortie est la longueur d'entrée divisée par trois, arrondie à l'entier supérieur, multipliée par quatre. Un, deux ou trois octets coûtent tous quatre caractères ; au-delà c'est 33 pour cent de surcoût à plat. La formule, pour quand il faut réserver des tampons ou rapporter la progression :
import 'dart:convert';
int encodedLength(int n) => (n + 2) ~/ 3 * 4;
void main() {
print(encodedLength(100000)); // 133336
}
La vitesse n'est pas la contrainte ; l'encodeur est un seul passage de table qui traite des mégaoctets en quelques millisecondes. Les contraintes sont l'impôt de taille lui-même, prélevé sur le câble et en mémoire, et le fait que la forme encodée est une chaîne. Gardez les deux en tête à grande échelle : pour des payloads qui peuvent devenir grands, encodez en stream comme montré plus haut plutôt que d'accumuler une grosse liste et une grosse chaîne, et pour des transferts répétés des mêmes données, demandez-vous si le canal a un mode binaire, car 33 pour cent est un supplément permanent qu'aucun algorithme ne peut rembourser.
L'encodeur en ligne de commande
La VM fait un CLI propre de l'encodeur. Cet outil lit un argument fichier ou l'entrée standard et affiche l'encodage d'alphabet standard :
import 'dart:convert';
import 'dart:io';
Future<void> main(List<String> args) async {
final bytes = await _read(args);
stdout.writeln(base64Encode(bytes));
}
Future<List<int>> _read(List<String> args) async {
if (args.isNotEmpty) {
return File(args[0]).readAsBytes();
}
final all = <int>[];
await for (final chunk in stdin) {
all.addAll(chunk);
}
return all;
}
Enregistrez-le sous bin/encode.dart et exécutez dart run bin/encode.dart photo.jpg > photo.b64, ou passez-le par un pipe avec cat config | dart run bin/encode.dart. Le compagnon, un décodeur qui lit et aplatit, est le premier exemple du guide de décodage, et ensemble les deux scripts sont une petite boîte à outils vraiment utile pour faire voyager du binaire par des canaux texte.
Les pièges qui mordent sur la sortie
- Le piège des codeUnits.
base64Encode(text.codeUnits)emballe des unités UTF-16, pas des octets ; ça marche pour l'ASCII et lèveArgumentErrorà la première unité de code au-dessus de 255. Encodez toujours le texte d'abord avec un encodage nommé. - Alphabet en décalage. Donner une sortie URL-safe à un consommateur qui attend l'alphabet standard, c'est un 400 qui attend son heure. Déterminez l'alphabet depuis la spec, encodez une fois, et ne convertissez pas après coup.
- Suppositions sur le padding. Dart met toujours le padding. Si la spec veut du sans padding, supprimez avec
replaceAll('=', '')comme étape explicite à la frontière, et dites-le dans le contrat. - Dérive du charset. Encoder des octets Latin-1 pour un consommateur qui décode en UTF-8 produit un base64 valide des mauvaises données. Rien ne lève d'exception ; le texte est juste faux.
- Encodage double. Encoder en base64 une valeur qui est déjà base64, un jeton copié depuis une autre config, est le bug classique du « se décode en un autre mur de base64 ».
- Illusion de confidentialité. Le base64 est un format, pas un chiffrement. Si le modèle de menace inclut un lecteur, la réponse est le chiffrement, pas l'encodage.
- Paquets obsolètes. Le
jwtde longue date sur pub.dev précède la null safety ; pour le travail JWT,cryptoplus les quelques lignes ci-dessus est la voie maintenue.
Quand saisir autre chose
- Envois de fichiers par HTTP. Utilisez
multipart/form-data; il porte des octets bruts, si bien que vous évitez l'impôt des 33 pour cent entièrement. - Payloads volumineux ou répétitifs. Compressez d'abord, encodez ensuite : le base64 de texte gzip est nettement plus petit que le base64 du texte, et le côté qui décompresse connaît déjà le format.
- Court texte dans les URL. L'encodage en pourcentage est plus court pour une poignée de caractères et garde la valeur lisible par un humain ; les data URIs le font même pour vous par défaut.
- Sorties de débogage et journaux. L'hexadécimal est 50 pour cent plus long que le base64 (le double de la taille brute, le base64 n'en fait que les quatre tiers) mais est bien plus facile à parcourir, à diff et à passer à un collègue ; pour des fragments binaires dans les journaux, il gagne souvent.
Bonnes pratiques, la liste de l'encodeur
- Encodez des octets, jamais des unités de code ; le texte passe d'abord par un encodage nommé.
- Choisissez l'alphabet depuis la spec du consommateur avant d'écrire l'appel.
- Supprimez le padding seulement là où la spec dit sans padding, comme étape visible à la frontière.
- Indiquez le charset explicitement dans le contrat ; ne supposez rien sur l'autre côté.
- Faites passer en stream tout ce qui peut devenir grand.
- Traitez le base64 comme un format pour canaux texte uniquement, jamais comme une protection pour des données sensibles.
Une brève histoire de deux alphabets
Le format que vous venez d'utiliser est plus ancien que toute version de Dart, et les choix d'alphabets à votre disposition ont été standardisés des décennies avant l'arrivée de Dart. La version courte :
- 1993, RFC 1521 : le MIME introduit le base64 comme encodage de transfert de contenu pour l'email, avec l'alphabet standard de 64 caractères et la limite de ligne de 76 caractères à laquelle cet article enveloppe. Le travail du format, porter du binaire par des canaux texte, date d'ici.
- 1996, RFC 2045 : la RFC qui rend obsolète le MIME et qui fait des règles de padding et de longueur de ligne du base64 le standard durable.
- 2006, RFC 4648 : l'encodage est sorti du MIME et standardisé à part, ajoutant l'alphabet URL-safe et le conseil aux décodeurs de rejeter les entrées invalides. Le choix des deux alphabets que vous avez dans Dart vient de ce document.
- 2015, RFC 7515 : JSON Web Signatures spécifie le base64url sans padding, la convention derrière chaque JWT.
- novembre 2015, Dart 1.13 : le base64 arrive dans
dart:convert; la variante URL-safe suit dans Dart 1.16 au printemps suivant, et les appels de premier niveaubase64Encodeetbase64UrlEncodeque vous avez utilisés plus haut atterrissent dans Dart 2.0 en 2018. - Aujourd'hui, Dart 3.13 : les deux alphabets, toujours avec padding, à un import de distance, la même machine stricte et simple depuis 2015.
Le surcoût de 33 pour cent n'a pas changé non plus depuis 1993. C'est une propriété du calcul, quatre symboles pour trois octets, et chaque implémentation que vous utiliserez jamais, dans chaque langage, le paie identiquement.
Faits amusants du banc d'encodage
- L'encodeur ne peut pas être coupé : il n'y a pas d'option pour une sortie sans padding dans le SDK, et c'est pourquoi « supprimer les pads » est toujours votre code, à votre frontière, en plein jour.
- Un octet devient quatre caractères :
base64Encode([65])donneQQ==. La chaîne base64 la plus courte possible fait quatre caractères, et seuls les deux premiers portent de l'information ; les deux derniers sont du padding. - Les deux encodeurs de Dart mettent le padding, y compris
base64UrlEncode. Le « sans padding » du base64url est une convention des consommateurs venue de la RFC 7515, pas une propriété de l'alphabet. - L'alphabet standard a été conçu pour être imprimable sur 7 bits, et il est resté la valeur par défaut depuis ; le fait que
+et/aient fini par gagner des remplaçants URL-safe est un signe de la place centrale qu'il a prise, pas un défaut. - Les mêmes 20 octets UTF-8 de
Héllo Wörld 世界s'emballent enSMOpbGxvIFfDtnJsZCDkuJbnlYw=, tandis que les 14 unités de code de la chaîne feraient planter l'encodeur à l'index 12. Mêmes caractères, deux sorties complètement différentes, dont une est une erreur. - Les fichiers PEM, les blocs
-----BEGIN CERTIFICATE-----de chaque certificat TLS, sont du base64 enveloppé à 64 caractères avec en-têtes, et le format date de 1987, six ans avant que le MIME ne publie le base64 pour l'email.
Vous avez maintenant tout le côté encodage : la surface API, la discipline octets d'abord, les décisions de padding et d'alphabet, et les schémas qui marchent pour les JWT, les data URIs, les fichiers, HTTP, le MIME, la configuration, les streams et le shell. La direction inverse, démonter l'une de ces chaînes, avec toute la stricte rigueur du décodeur, sa surprise d'échappement en pourcentage et ses outils de réparation, est couverte dans le guide de décodage Base64, lié juste en dessous.
Dernière mise à jour : 2026-09-08
Article associé : Décodage Base64 en Dart : un guide complet