Pourquoi atob() renvoie des octets et btoa() rejette du texte

Comprenez les chaînes binaires JavaScript, les erreurs de plage de caractères et l'étape UTF-8 nécessaire autour d'atob et de btoa.

Les noms atob et btoa peut suggérer une conversion de texte ordinaire. Le contrat de leur navigateur est plus étroit : ils convertissent entre le texte Base64 et une chaîne dont les valeurs de caractères représentent des octets individuels. Cette chaîne d'octets est un conteneur et non une promesse que le résultat sera un texte Unicode lisible.

Deux cordes différentes

Les chaînes JavaScript utilisent les unités de code UTF-16. Une chaîne d'octets restreint délibérément chaque valeur de caractère entre 0 et 255. btoa('Hello') fonctionne parce que les valeurs ASCII correspondent à cette plage. btoa('你好') échoue parce que ces caractères ne tiennent pas dans un octet. Même un caractère qui tient dans un octet peut prêter à confusion : btoa('é') encode l'octet unique E9, et non les UTF-8 octets C3 A9 attendus par de nombreux services.

console.log(btoa('é')); // 6Q== (byte E9)
console.log(btoa(String.fromCharCode(0xc3, 0xa9))); // w6k= (UTF-8)

Mettez la conversion UTF-8 autour des fonctions d'octet

TextEncoder produit UTF-8 octets à partir du texte. Après décodage de Base64, un message fatal TextDecoder convertit les octets UTF-8 valides en texte et rejette les séquences non valides au lieu de les remplacer silencieusement. Ce petit exemple est volontairement explicite afin que la limite soit visible.

const text = '中文😊';
const bytes = new TextEncoder().encode(text);
let binary = '';
for (const byte of bytes) binary += String.fromCharCode(byte);
const encoded = btoa(binary);
console.log(encoded); // 5Lit5paH8J+Yig==
const decodedBytes = Uint8Array.from(atob(encoded), c => c.charCodeAt(0));
const decoded = new TextDecoder('utf-8', { fatal: true }).decode(decodedBytes);
console.log(decoded); // 中文😊

Ne traitez pas un résultat binaire comme une chaîne brisée

atob('/w==') représente l'octet FF. Ce n'est pas UTF-8 valide en soi, mais ce sont des données binaires parfaitement valides. L'action suivante correcte peut être un téléchargement d'octets ou une vue hexadécimale, et non une autre astuce de conversion de texte. Exécuter cette chaîne d'octets TextEncoder coderait le caractère U+00FF et modifierait les octets.

Le décodeur du navigateur n'est pas non plus un validateur de protocole complet. Les espaces acceptés et les remplissages omis peuvent différer des exigences strictes de l’application. Si un protocole nécessite un codage canonique, validez séparément l'alphabet, le remplissage et les bits inutilisés. Pour les fichiers volumineux, évitez de répartir un tableau d'octets complet dans String.fromCharCode(...bytes); les limites du nombre d'arguments peuvent échouer bien avant que le fichier ne devienne inhabituellement volumineux.

Lire les contrats du navigateur dans MDN : atob() et MDN : btoa(). L'application doit décider si elle souhaite une conversion permissive, un validateur strict ou un flux de travail de préservation des fichiers, plutôt que de supposer qu'il s'agit de la même tâche.