Charger un fichier
  1. base64hub
  2. Base64 dans le code
  3. Node.js
Code

Base64 en Node.js

Node.js fait du Base64 via Buffer, avec base64 et base64url comme encodages intégrés. Toutes les sorties ci-dessous proviennent de Node.js 24.

Encoder et décoder une chaîne

Buffer.from(string, "utf8") transforme du texte en octets UTF-8, et .toString("base64") les encode. Le décodage reprend les deux mêmes appels dans l’autre sens.

const enc = Buffer.from("héllo wörld", "utf8").toString("base64");
console.log(enc);                                         // aMOpbGxvIHfDtnJsZA==

console.log(Buffer.from(enc, "base64").toString("utf8")); // héllo wörld

"utf8" est la valeur par défaut des deux appels, mais l’écrire rend l’intention évidente. Buffer est une sous-classe de Uint8Array : un simple Uint8Array doit donc être enveloppé. u8.toString("base64") ignore l’argument et renvoie "104,105", tandis que Buffer.from(u8).toString("base64") renvoie aGk=.

Base64 URL-safe (base64url)

Node a un encodage dédié à l’alphabet URL-safe. Il n’écrit jamais de remplissage :

Buffer.from("<<???>>").toString("base64");     // PDw/Pz8+Pg==
Buffer.from("<<???>>").toString("base64url");  // PDw_Pz8-Pg
Buffer.from("PDw_Pz8-Pg", "base64url").toString();  // <<???>>

Si un destinataire veut du Base64 URL-safe avec remplissage, ajoutez-le vous-même : s + "=".repeat((4 - s.length % 4) % 4).

Au décodage, Node est accommodant : "base64" comme "base64url" acceptent les deux alphabets et ne se soucient pas du remplissage.

Buffer.from("PDw_Pz8-Pg==", "base64").toString();   // <<???>>
Buffer.from("PDw/Pz8+Pg==", "base64url").toString(); // <<???>>

C’est pratique pour les JWT et les jetons (essayez le décodeur JWT), mais lisez la section sur la validation ci-dessous. Pour le contexte sur les deux alphabets : Base64 ou Base64URL.

Encoder et décoder un fichier

fs accepte "base64" comme encodage, ce qui économise une étape dans les deux sens :

import { readFileSync, writeFileSync } from "node:fs";

readFileSync("hello.txt").toString("base64");  // aGVsbG8K
readFileSync("hello.txt", "base64");           // aGVsbG8K  (same thing)

writeFileSync("copy.txt", Buffer.from("aGVsbG8K", "base64"));
writeFileSync("copy2.txt", "aGVsbG8K", "base64");  // decodes while writing

Pour intégrer un fichier en HTML ou en CSS, ajoutez un préfixe au résultat : `data:image/png;base64,${b64}`. Le générateur de data URI construit la même chaîne dans le navigateur.

Gros fichiers en flux

Charger un fichier de 2 Go en mémoire pour l’encoder est une mauvaise idée. Un flux Transform peut encoder morceau par morceau, à condition de n’encoder que des groupes complets de 3 octets et de reporter le reste sur le morceau suivant :

import { createReadStream, createWriteStream } from "node:fs";
import { pipeline } from "node:stream/promises";
import { Transform } from "node:stream";

class Base64Encode extends Transform {
  constructor() { super(); this.rest = Buffer.alloc(0); }
  _transform(chunk, _enc, cb) {
    const buf = Buffer.concat([this.rest, chunk]);
    const cut = buf.length - (buf.length % 3);
    this.rest = buf.subarray(cut);
    cb(null, buf.subarray(0, cut).toString("base64"));
  }
  _flush(cb) { cb(null, this.rest.toString("base64")); }
}

await pipeline(createReadStream("big.bin"), new Base64Encode(),
               createWriteStream("big.b64"));

Sur un fichier aléatoire de 1 Mo, la sortie correspondait exactement à readFileSync("big.bin").toString("base64"). Sans ce report, chaque limite de morceau qui n’est pas un multiple de 3 insérerait un remplissage = au milieu de la sortie. La raison est expliquée dans le fonctionnement de Base64.

Pour connaître la taille décodée sans décoder, utilisez Buffer.byteLength("aGVsbG8K", "base64"), qui renvoie 6.

Valider une entrée

Buffer.from(s, "base64") ne lève jamais d’erreur sur un contenu invalide. Il supprime les caractères qui n’appartiennent à aucun des deux alphabets et renvoie ce qui reste :

Buffer.from("aGk=!!", "base64");             // <Buffer 68 69>
Buffer.from("not base64 at all", "base64");  // <Buffer 9e 8b 5b 6a c7 ba e1 ab 5a 96>
Buffer.from("aGk", "base64").toString();     // hi  (missing padding is fine)

Pour une entrée non fiable, vérifiez l’alphabet et la longueur, puis réencodez et comparez. L’aller-retour détecte les bits de remplissage non nuls, comme dans aGl=, qui se décode lui aussi en hi mais n’est pas canonique.

function isBase64(s) {
  return /^[A-Za-z0-9+/]*={0,2}$/.test(s) && s.length % 4 === 0 &&
         Buffer.from(s, "base64").toString("base64") === s;
}

isBase64("aGk=");        // true
isBase64("aGk");         // false (no padding)
isBase64("aGl=");        // false (non-canonical)
isBase64("not base64");  // false

Pour base64url, adaptez l’expression régulière en [A-Za-z0-9_-] et supprimez le contrôle de longueur. Le validateur Base64 effectue les mêmes contrôles de façon interactive.

Pièges et messages d’erreur

  • Mauvais type d’argument. Buffer.from(123, "base64") lève TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received type number (123). En général, un undefined ou un nombre s’est glissé depuis du JSON.
  • new Buffer(). Le constructeur est obsolète (DEP0005) pour des raisons de sécurité. Utilisez Buffer.from ou Buffer.alloc.
  • Décoder du binaire comme du texte. .toString("utf8") sur les octets d’une image produit des caractères de remplacement (U+FFFD) sans erreur, et le fichier est corrompu quand vous le réécrivez. Gardez les données binaires dans un Buffer.
  • Le préfixe de data URI. Buffer.from("data:image/png;base64,iVBOR...", "base64") n’échouera pas : il décode le préfixe comme des octets parasites. Coupez d’abord à la virgule.
  • Les sauts de ligne. Node ignore \n et \r au décodage, donc une entrée découpée au format MIME passe, mais l’encodeur ne coupe jamais les lignes. Utilisez b64.match(/.{1,76}/g).join("\r\n") quand il vous faut des lignes MIME.

Notes de version

  • Encodage base64url : Node.js 15.7.0 et 14.18.0.
  • Globales btoa et atob : Node.js 16.0, classées comme héritées.
  • Uint8Array.prototype.toBase64 et Uint8Array.fromBase64 ne sont pas disponibles dans Node.js 24 (typeof renvoie "undefined"). Elles arriveront avec des versions plus récentes de V8 ; d’ici là, Buffer reste la solution.
  • Buffer.from(string, "base64") ignore les espaces et les caractères invalides dans toutes les versions : ne comptez pas sur lui pour valider.

FAQ

Buffer.from(str, 'base64') lève-t-il une erreur sur une entrée invalide ?

Non. Node saute les caractères hors de l’alphabet et s’arrête au premier = : des données parasites se décodent en quelques octets, sans la moindre erreur. Validez vous-même la chaîne si l’entrée n’est pas fiable.

Quelle version de Node.js a ajouté base64url ?

L’encodage base64url est arrivé dans Node.js 15.7.0 et a été rétroporté dans 14.18.0. Sur n’importe quelle version actuelle, vous pouvez compter dessus.

Faut-il utiliser btoa dans Node.js ?

Node classe btoa et atob comme hérités et recommande Buffer. Ils existent pour la compatibilité avec les navigateurs et partagent la limitation au Latin-1 décrite dans Base64 en JavaScript.

Continuer la lecture