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

Base64 en JavaScript

Les navigateurs proposent btoa et atob, limités au Latin-1, ainsi que les plus récents Uint8Array.toBase64() / fromBase64(), qui gèrent correctement les octets. Les sorties ci-dessous proviennent de Chromium headless.

btoa et atob : Latin-1 uniquement

btoa (« binary to ASCII ») encode une chaîne dont chaque caractère est traité comme un octet. atob fait l’inverse. Cela fonctionne pour l’ASCII :

btoa("hello");        // "aGVsbG8="
atob("aGVsbG8=");     // "hello"

Tout ce qui sort des points de code 0 à 255 casse :

btoa("héllo ✓");
// InvalidCharacterError: Failed to execute 'btoa' on 'Window':
// The string to be encoded contains characters outside of the Latin1 range.

Pire encore, les caractères qui tiennent en Latin-1 s’encodent sans erreur, mais avec les mauvais octets. btoa("héllo") renvoie aOlsbG8= (é en Latin-1 = 0xE9), alors que tous les autres langages et l’encodeur Base64 produisent la version UTF-8 aMOpbGxv.

btoa et atob existent aussi dans Node.js 16+ en tant que globales héritées, mais côté serveur, l’API Buffer est plus rapide et propose un encodage base64url intégré. Voir Base64 en Node.js.

Encoder et décoder des chaînes UTF-8

La solution portable consiste à transformer la chaîne en octets UTF-8 avec TextEncoder, puis à passer ces octets à btoa sous forme de « chaîne binaire » :

function encodeUtf8(str) {
  const bytes = new TextEncoder().encode(str);
  let bin = "";
  for (const b of bytes) bin += String.fromCharCode(b);
  return btoa(bin);
}

function decodeUtf8(b64) {
  const bin = atob(b64);
  const bytes = Uint8Array.from(bin, c => c.charCodeAt(0));
  return new TextDecoder().decode(bytes);
}

encodeUtf8("héllo wörld");            // "aMOpbGxvIHfDtnJsZA=="
decodeUtf8("aMOpbGxvIHfDtnJsZA==");   // "héllo wörld"
atob("aMOpbGxvIHfDtnJsZA==");         // "héllo wörld"  (no TextDecoder)

L’ancienne astuce btoa(unescape(encodeURIComponent(str))) donne le même résultat, mais repose sur unescape, qui est obsolète.

Uint8Array.toBase64 et fromBase64

La nouvelle API travaille directement sur les octets, sans passer par une chaîne binaire. Elle est prise en charge par Chrome et Edge 140+, Firefox 133+ et Safari 18.2+.

const bytes = new TextEncoder().encode("héllo wörld");
bytes.toBase64();                                      // "aMOpbGxvIHfDtnJsZA=="

new TextDecoder().decode(Uint8Array.fromBase64("aMOpbGxvIHfDtnJsZA=="));
// "héllo wörld"

Il existe aussi setFromBase64, qui décode dans un tampon existant et indique la place utilisée :

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

Détectez sa présence avant de compter dessus : typeof Uint8Array.prototype.toBase64 === "function". Le guide Uint8Array.toBase64 passe toutes les options en revue.

Base64 URL-safe avec et sans remplissage

Avec la nouvelle API, il suffit de passer des options :

const d = new TextEncoder().encode("<<???>>");
d.toBase64({ alphabet: "base64url" });                     // "PDw_Pz8-Pg=="
d.toBase64({ alphabet: "base64url", omitPadding: true });  // "PDw_Pz8-Pg"
Uint8Array.fromBase64("PDw_Pz8-Pg", { alphabet: "base64url" }); // bytes of "<<???>>"

Avec btoa/atob, convertissez l’alphabet vous-même :

const toB64Url = b64 =>
  b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");

const fromB64Url = s => {
  s = s.replace(/-/g, "+").replace(/_/g, "/");
  return s + "=".repeat((4 - s.length % 4) % 4);
};

toB64Url(btoa("<<???>>"));        // "PDw_Pz8-Pg"
atob(fromB64Url("PDw_Pz8-Pg"));   // "<<???>>"

C’est ce qu’utilisent les JWT ; voir Base64 ou Base64URL ou collez un jeton dans le décodeur JWT.

Fichiers, Blobs et gros volumes

Pour lire un fichier choisi par l’utilisateur, FileReader.readAsDataURL renvoie une data URI. Supprimez tout jusqu’à la virgule pour obtenir du Base64 brut :

const reader = new FileReader();
reader.onload = () => {
  const dataUrl = reader.result;           // "data:text/plain;base64,aGVsbG8K"
  const b64 = dataUrl.slice(dataUrl.indexOf(",") + 1);
};
reader.readAsDataURL(file);

Avec la nouvelle API, c’est plus court : new Uint8Array(await file.arrayBuffer()).toBase64().

Pour retransformer du Base64 en fichier téléchargeable, décodez-le en octets et enveloppez-les dans un Blob :

const bytes = Uint8Array.fromBase64(b64);
const url = URL.createObjectURL(new Blob([bytes], { type: "application/pdf" }));

Pour les gros tampons, ne les étalez jamais dans String.fromCharCode(...bytes). Avec 500 000 octets, Chromium lève RangeError: Maximum call stack size exceeded. Construisez plutôt la chaîne binaire par tranches :

function bytesToBase64(bytes) {
  let bin = "";
  for (let i = 0; i < bytes.length; i += 0x8000) {
    bin += String.fromCharCode.apply(null, bytes.subarray(i, i + 0x8000));
  }
  return btoa(bin);
}
// 5,000,000 bytes -> 6,666,668 characters

La sortie est environ un tiers plus grande que l’entrée ; le guide du surcoût de taille donne la formule exacte.

Validation et messages d’erreur

atob ignore les espaces ASCII et tolère un remplissage manquant : atob(" aG k= ") et atob("aGk") renvoient tous deux "hi". Il rejette les caractères hors de l’alphabet standard :

atob("PDw_Pz8-Pg");
// InvalidCharacterError: Failed to execute 'atob' on 'Window':
// The string to be decoded is not correctly encoded.

fromBase64 lève une SyntaxError et peut être rendu plus strict avec lastChunkHandling :

Uint8Array.fromBase64("PDw_Pz8-Pg");
// SyntaxError: Found a character that cannot be part of a valid base64 string.

Uint8Array.fromBase64("aGl=");                                   // 2 bytes, accepted
Uint8Array.fromBase64("aGl=", { lastChunkHandling: "strict" });
// SyntaxError: The base64 input terminates with non-zero padding bits.

La valeur par défaut, "loose", accepte un remplissage manquant. "strict" exige un remplissage correct et des bits de remplissage à zéro ; "stop-before-partial" laisse de côté un dernier morceau incomplet, pour les décodeurs en flux. Le texte des messages varie d’un navigateur à l’autre, mais les noms d’erreur sont standard. Pour un contrôle visuel, le validateur Base64 signale le problème exact, et les erreurs Base64 courantes en listent les causes.

FAQ

Pourquoi btoa lève-t-il InvalidCharacterError ?

btoa n’accepte que les caractères de la plage Latin-1 (points de code 0 à 255). Le moindre emoji, caractère cyrillique ou CJK provoque l’exception. Convertissez d’abord la chaîne en octets UTF-8 avec TextEncoder, ou utilisez Uint8Array.prototype.toBase64().

Peut-on utiliser Uint8Array.toBase64 en production ?

La méthode est disponible dans les versions actuelles de Chrome, Edge, Firefox et Safari, mais pas dans les navigateurs plus anciens ni dans Node.js 24. Détectez-la avec typeof Uint8Array.prototype.toBase64 === "function" et rabattez-vous sur la fonction d’aide basée sur btoa. Détails dans Uint8Array.toBase64 expliqué.

Pourquoi atob renvoie-t-il des caractères bizarres comme é au lieu de é ?

atob renvoie un caractère par octet : une séquence UTF-8 de deux octets devient deux caractères Latin-1. Passez le résultat dans Uint8Array.from(bin, c => c.charCodeAt(0)) puis TextDecoder pour obtenir un texte correct.

Comment décoder du Base64URL avec atob ?

Remplacez - par + et _ par /, puis complétez avec = jusqu’à un multiple de 4 caractères. Avec la nouvelle API, appelez Uint8Array.fromBase64(s, { alphabet: "base64url" }) et sautez ces deux étapes.

Continuer la lecture