Charger un fichier
  1. base64hub
  2. Apprendre Base64
  3. Uint8Array.toBase64()
Guide

Uint8Array.toBase64 et fromBase64 en JavaScript

JavaScript a enfin une API Base64 qui travaille sur des octets, gère Base64URL et vous laisse choisir la rigueur du décodage. Elle remplace la plupart des usages de btoa, atob et des boucles écrites à la main.

Encoder et décoder en une ligne

L’API ajoute trois méthodes pour Base64 et trois pour l’hex :

MéthodeRôle
bytes.toBase64(options)Uint8Array vers une chaîne Base64
Uint8Array.fromBase64(string, options)Chaîne Base64 vers un nouveau Uint8Array
bytes.setFromBase64(string, options)Décode dans un Uint8Array existant
bytes.toHex()Uint8Array vers une chaîne hex en minuscules
Uint8Array.fromHex(string)Chaîne hex vers un nouveau Uint8Array
bytes.setFromHex(string)Décode de l’hex dans un Uint8Array existant

Le texte doit d’abord devenir des octets. TextEncoder produit toujours de l’UTF-8 : Unicode fonctionne donc sans astuce.

const bytes = new TextEncoder().encode("Grüße, 世界");
const b64 = bytes.toBase64();
console.log(b64); // "R3LDvMOfZSwg5LiW55WM"

const back = new TextDecoder().decode(Uint8Array.fromBase64(b64));
console.log(back); // "Grüße, 世界"

Un ArrayBuffer, par exemple issu de fetch().arrayBuffer() ou de crypto.subtle.digest(), a besoin d’une vue : new Uint8Array(buffer).toBase64(). Les autres tableaux typés n’ont pas du tout ces méthodes. Pour encoder la mémoire brute d’un Uint16Array, enveloppez-la : new Uint8Array(u16.buffer, u16.byteOffset, u16.byteLength).

Options : alphabet, omitPadding, lastChunkHandling

alphabet

"base64" (par défaut) utilise + et /. "base64url" utilise - et _. L’option s’applique à l’encodage comme au décodage, et un décodeur réglé sur un alphabet rejette les caractères de l’autre.

const data = new Uint8Array([251, 255]);
data.toBase64();                                         // "+/8="
data.toBase64({ alphabet: "base64url" });                // "-_8="
data.toBase64({ alphabet: "base64url", omitPadding: true }); // "-_8"

Uint8Array.fromBase64("-_8", { alphabet: "base64url" }); // Uint8Array [251, 255]
Uint8Array.fromBase64("-_8");                            // SyntaxError

omitPadding

toBase64 ajoute le remplissage = par défaut, même avec l’alphabet Base64URL. Passez omitPadding: true pour une sortie de style JWT. Au décodage, le remplissage est géré par lastChunkHandling.

lastChunkHandling

Cette option décide de ce que fromBase64 et setFromBase64 font du dernier groupe de caractères. Les trois modes, testés avec une entrée qui se décode en octets de Hi?> :

Entrée"loose" (par défaut)"strict""stop-before-partial"
SGk/Pg==Hi?>Hi?>Hi?>
SGk/Pg (sans remplissage)Hi?>SyntaxErrorHi? (s’arrête avant Pg)
SGk/Ph== (bits de bourrage non nuls)Hi?>SyntaxErrorHi?>
SGk/P (un caractère restant)SyntaxErrorSyntaxErrorHi?
  • loose se comporte comme atob : le remplissage est facultatif, et les bits restants du dernier caractère sont ignorés.
  • strict n’accepte que des entrées canoniques avec remplissage, telles que les décrit la RFC 4648. Utilisez-le quand la forme encodée doit être unique, par exemple quand des chaînes Base64 sont comparées ou signées.
  • stop-before-partial décode tous les groupes complets de 4 caractères et laisse de côté une fin incomplète. C’est prévu pour le streaming, où le reste du groupe arrive dans le morceau suivant.

Dans tous les modes, les espaces ASCII (espaces, tabulations, sauts de ligne) sont ignorés : une entrée coupée façon MIME se décode donc directement. Tout autre caractère hors de l’alphabet lève une SyntaxError.

Écrire dans un buffer existant

setFromBase64 et setFromHex décodent dans un Uint8Array dont vous disposez déjà et renvoient ce qu’ils ont consommé :

const target = new Uint8Array(8);
const result = target.setFromBase64("SGVsbG8gd29ybGQ=");
console.log(result); // { read: 8, written: 6 }

Hello world fait 11 octets et la cible en contient 8. Le décodeur n’écrit que des groupes complets de 3 octets qui tiennent : il s’arrête donc après 6 octets et indique avoir lu 8 caractères. Rappelez-le avec le reste de la chaîne et un subarray de la cible pour continuer. Combiné à stop-before-partial, cela permet de décoder un flux morceau par morceau dans un buffer préalloué :

const out = new Uint8Array(1024);
const { read, written } = out.setFromBase64("SGk/Pg", {
  lastChunkHandling: "stop-before-partial",
});
// read: 4, written: 3. Keep "Pg" and prepend it to the next chunk.

Les méthodes hex suivent le même principe. toHex() écrit toujours en minuscules, fromHex() accepte les deux casses, et un nombre impair de chiffres provoque une SyntaxError :

new Uint8Array([0xde, 0xad, 0xbe, 0xef]).toHex(); // "deadbeef"
Uint8Array.fromHex("CAFE");                       // Uint8Array [202, 254]

Pourquoi btoa et atob sont malcommodes

btoa et atob datent des débuts des navigateurs et travaillent sur des « chaînes binaires » : des chaînes JavaScript où chaque code de caractère est une valeur d’octet de 0 à 255. Cela pose trois problèmes.

Unicode lève une exception ou se dégrade en silence. Les caractères au-delà de U+00FF font lever une InvalidCharacterError à btoa (Chrome : « The string to be encoded contains characters outside of the Latin1 range »). Les caractères entre U+0080 et U+00FF sont pires : ils s’encodent sans erreur, en Latin-1. btoa("Grüße") renvoie R3L832U=, alors que l’encodage UTF-8 qu’attendent tous les autres systèmes est R3LDvMOfZQ==.

Le décodage renvoie une chaîne, pas des octets. atob("Q2Fmw6k=") renvoie "Café". Pour obtenir le vrai texte, il faut convertir la chaîne en octets puis décoder ceux-ci en UTF-8, ce qui demande une boucle ou Uint8Array.from(atob(s), c => c.charCodeAt(0)).

Les grandes entrées demandent des précautions. Le one-liner courant btoa(String.fromCharCode(...bytes)) passe chaque octet comme argument de fonction, et avec un tableau de 1 Mo il lève RangeError: Maximum call stack size exceeded. Il faut construire la chaîne par morceaux.

Il n’y a pas non plus de prise en charge de Base64URL, ni de contrôle sur le remplissage ou la rigueur. L’ancienne astuce btoa(unescape(encodeURIComponent(text))) produit bien du Base64 UTF-8, mais repose sur unescape, déprécié. Avec la nouvelle API, tout cela devient new TextEncoder().encode(text).toBase64().

Prise en charge par les navigateurs et Node.js

Les méthodes viennent de la proposition TC39 « Uint8Array to/from Base64 », passée au stade 4 en juillet 2025 et intégrée à ECMAScript 2026.

EnvironnementPremière version avec les six méthodes
Chrome, Edge140
Firefox133
Safari (macOS et iOS)18.2
Node.js25.0.0

Avec Chrome 140, la fonctionnalité est devenue Baseline en septembre 2025 : tous les grands navigateurs actuels l’ont donc. Dans Node.js, nous avons testé les méthodes directement. Node 25.0.0 et 26.12.1 ont les six. Node 24.22.1, une version LTS, n’en a aucune par défaut. Son V8 accepte un flag expérimental --js-base-64, mais dans notre test ce flag exposait toBase64, fromBase64, toHex et fromHex sans setFromBase64 ni setFromHex : considérez donc Node 24 comme non pris en charge. Sur Node 24 et antérieur, utilisez Buffer.from(b64, "base64") et buf.toString("base64"), qui acceptent aussi "base64url". Deno 2.5 et Bun 1.1.22 prennent également en charge l’API, d’après les données de compatibilité de MDN.

Une solution de repli pour les anciens environnements

Détectez la fonctionnalité et rabattez-vous sur btoa/atob avec un découpage en morceaux. Cette version a été testée sur Node 24 (chemin de repli) et Node 26 (chemin natif) avec du texte UTF-8 et un tableau de 1 Mo :

function bytesToBase64(bytes) {
  if (typeof bytes.toBase64 === "function") return bytes.toBase64();
  let bin = "";
  for (let i = 0; i < bytes.length; i += 0x8000) {
    bin += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
  }
  return btoa(bin);
}

function base64ToBytes(b64) {
  if (typeof Uint8Array.fromBase64 === "function") return Uint8Array.fromBase64(b64);
  return Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
}

Le repli ne couvre que les options par défaut. Si vous avez besoin de base64url, de strict ou de setFromBase64 partout, utilisez un polyfill complet : core-js en fournit un sous la forme de es.uint8-array.to-base64, es.uint8-array.from-base64 et de modules apparentés.

Pour essayer des encodages sans écrire de code, utilisez l’encodeur Base64 et le décodeur, ou Base64URL pour l’alphabet URL-safe. D’autres exemples JavaScript, dont le Buffer de Node, se trouvent dans Base64 en JavaScript et Base64 en Node.js.

FAQ

Peut-on utiliser la nouvelle API Base64 en production dès aujourd'hui ?

Dans les navigateurs, oui : elle est disponible dans Chrome et Edge 140, Firefox 133 et Safari 18.2. Dans Node.js, il faut la version 25 ou ultérieure. Gardez une solution de repli si vous prenez en charge des navigateurs plus anciens ou Node 24 et antérieur.

Uint8Array.fromBase64 accepte-t-il le Base64URL ?

Oui, avec { alphabet: "base64url" }. Sans cette option, - et _ provoquent une SyntaxError. Le remplissage est facultatif dans le mode loose par défaut, pour les deux alphabets.

Comment encoder une chaîne avec toBase64 ?

Convertissez-la d’abord en octets UTF-8 : new TextEncoder().encode(text).toBase64(). Pour décoder, faites l’inverse avec new TextDecoder().decode(Uint8Array.fromBase64(b64)).

Pourquoi toBase64 n'est-il pas une fonction sur mon Buffer ou mon Uint16Array ?

Les méthodes n’existent que sur Uint8Array et ses sous-classes. Le Buffer de Node les reçoit à partir de Node 25. Pour les autres tableaux typés, enveloppez la mémoire : new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength).

Continuer la lecture