Base64 dans le navigateur et Node.js : contrat d'un octet, deux API

Utilisez TextEncoder dans les navigateurs et Buffer dans Node.js, puis testez leurs sorties avec les mêmes appareils Unicode et binaires.

Un client de navigateur et un service Node.js doivent produire le même Base64 pour les mêmes octets. Ils n’ont pas besoin d’utiliser le même API pour le faire. L'accord important est la représentation en octets du texte, l'alphabet et le traitement du remplissage et des entrées invalides.

Navigateur : rendre explicite l'étape UTF-8

Les fonctions traditionnelles du navigateur fonctionnent sur des chaînes d'octets. Convertir du texte Unicode avec TextEncoder, puis codez ces octets. Sur le chemin de retour, reconstruisez un tableau d'octets et décodez explicitement UTF-8. Cet exemple utilise une boucle pour éviter d'étendre un grand tableau en arguments de fonction, bien qu'un encodeur de fichier de production doive toujours utiliser des morceaux délimités ou un octet approprié API.

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); // 中文😊

Node.js : conserver Buffer à la limite d'octet

Buffer.from(text, 'utf8') rend visible l’encodage du texte. Appel toString('base64') représente alors ces octets. Le décodage vers un Buffer préserve les données binaires ; la conversion immédiate en chaîne UTF-8 n'est appropriée que lorsque la charge utile est connue pour être du texte.

import { Buffer } from 'node:buffer';
const encoded = Buffer.from('中文😊', 'utf8').toString('base64');
console.log(encoded); // 5Lit5paH8J+Yig==
const bytes = Buffer.from(encoded, 'base64');
console.log(new TextDecoder('utf-8', { fatal: true }).decode(bytes)); // 中文😊

L’option fatal de TextDecoder est utilisée volontairement dans cet exemple. La conversion habituelle d’un Buffer en chaîne UTF-8 peut insérer des caractères de remplacement lorsque l’entrée est invalide. Un affichage réussi masquerait alors le fait que les octets d’origine n’ont pas pu être représentés fidèlement.

Convenir séparément de la validation

Le décodeur Base64 de Node accepte certains formulaires d'entrée qu'une application plus stricte peut rejeter. N'utilisez pas une conversion Buffer réussie comme preuve que la représentation canonique d'un protocole a été fournie. Si la syntaxe exacte est importante pour les signatures, les clés de cache ou la sérialisation interopérable, vérifiez-la avant le décodage.

Pour un test de régression commun, incluez du texte ASCII, du chinois, des emoji, un octet nul, l’octet FF et toutes les longueurs proches d’une limite de trois octets. Comparez les octets entre les environnements d’exécution. Testez également la sortie Base64URL avec un jeu de données contenant réellement les caractères remplacés ; une entrée dont la sortie ne contient que des lettres ne permet pas de vérifier le fonctionnement de la variante.

Lors de la lecture de fichiers dans Node, conservez le Buffer renvoyé plutôt que d'appliquer un encodage de texte au fichier lu. Dans le navigateur, utilisez File.arrayBuffer(). Ces petits choix de limites évitent la plupart des corruptions accidentelles. Voir Node.js Buffer pour le comportement du nœud et MDN : btoa() pour le contrat de chaîne d'octets du navigateur.