Enviar um arquivo
  1. base64hub
  2. Base64 no código
  3. JavaScript
Código

Base64 em JavaScript

Os navegadores têm btoa e atob, que só lidam com Latin-1, e os mais novos Uint8Array.toBase64() / fromBase64(), que tratam bytes direito. As saídas abaixo vêm do Chromium headless.

btoa e atob: só Latin-1

O btoa (“binary to ASCII”) codifica uma string em que cada caractere é tratado como um byte. O atob faz o inverso. Isso funciona com ASCII:

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

E quebra com qualquer coisa fora dos code points 0–255:

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

Pior: caracteres que cabem no Latin-1 são codificados sem erro, mas como os bytes errados. btoa("héllo") retorna aOlsbG8= (é em Latin-1 = 0xE9), enquanto todas as outras linguagens e o codificador Base64 geram a versão UTF-8 aMOpbGxv.

O btoa e o atob também existem no Node.js 16+ como globais legados, mas no servidor a API Buffer é mais rápida e tem a codificação base64url embutida. Veja Base64 em Node.js.

Codificar e decodificar strings UTF-8

A solução portável é transformar a string em bytes UTF-8 com TextEncoder e passar esses bytes ao btoa como uma “string binária”:

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)

O velho truque btoa(unescape(encodeURIComponent(str))) dá o mesmo resultado, mas depende do unescape, que está obsoleto.

Uint8Array.toBase64 e fromBase64

A API mais nova trabalha direto com bytes, sem a etapa da string binária. Ela é suportada no Chrome e no Edge 140+, no Firefox 133+ e no 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"

Há também o setFromBase64, que decodifica para um buffer existente e informa quanto usou:

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

Detecte o recurso antes de depender dele: typeof Uint8Array.prototype.toBase64 === "function". O guia do Uint8Array.toBase64 cobre todas as opções.

Base64 URL-safe com e sem preenchimento

Com a API nova, passe opções:

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 "<<???>>"

Com btoa/atob, traduza o alfabeto você mesmo:

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"));   // "<<???>>"

É isso que os JWTs usam; veja Base64 vs Base64URL ou cole um token no decodificador de JWT.

Arquivos, Blobs e dados grandes

Para ler um arquivo escolhido pelo usuário, o FileReader.readAsDataURL retorna uma data URI. Remova tudo até a vírgula para ter o Base64 puro:

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);

Com a API nova fica mais curto: new Uint8Array(await file.arrayBuffer()).toBase64().

Para transformar o Base64 de volta num arquivo para download, decodifique para bytes e envolva-os num Blob:

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

Com buffers grandes, nunca os espalhe em String.fromCharCode(...bytes). Com 500.000 bytes, o Chromium lança RangeError: Maximum call stack size exceeded. Monte a string binária em fatias:

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

A saída fica cerca de um terço maior que a entrada; o guia de aumento de tamanho traz a fórmula exata.

Validação e mensagens de erro

O atob ignora espaços em branco ASCII e tolera preenchimento ausente, então atob(" aG k= ") e atob("aGk") retornam "hi". Ele rejeita caracteres fora do alfabeto padrão:

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

O fromBase64 lança SyntaxError e pode ficar mais rígido com 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.

O padrão, "loose", aceita preenchimento ausente. O "strict" exige preenchimento correto e bits de preenchimento zero; o "stop-before-partial" deixa um bloco final incompleto para decodificadores em streaming. O texto das mensagens muda de navegador para navegador, mas os nomes dos erros são padronizados. Para uma conferência visual, o validador de Base64 destaca o problema exato, e erros comuns de Base64 lista as causas.

Perguntas frequentes

Por que o btoa lança InvalidCharacterError?

O btoa só aceita caracteres da faixa Latin-1 (code points 0–255). Qualquer emoji, caractere cirílico ou CJK faz ele lançar o erro. Converta antes a string em bytes UTF-8 com TextEncoder ou use Uint8Array.prototype.toBase64().

Posso usar Uint8Array.toBase64 em produção?

Ele já vem nas versões atuais do Chrome, Edge, Firefox e Safari, mas não em navegadores antigos nem no Node.js 24. Detecte o recurso com typeof Uint8Array.prototype.toBase64 === "function" e caia para o helper com btoa. Detalhes em Uint8Array.toBase64 explicado.

Por que o atob retorna caracteres estranhos como é em vez de é?

O atob retorna um caractere por byte, então uma sequência UTF-8 de dois bytes vira dois caracteres Latin-1. Passe o resultado por Uint8Array.from(bin, c => c.charCodeAt(0)) e TextDecoder para obter o texto correto.

Como decodificar Base64URL com atob?

Troque - por + e _ por / e complete com = até um múltiplo de 4 caracteres. Com a API nova, chame Uint8Array.fromBase64(s, { alphabet: "base64url" }) e pule as duas etapas.

Continue lendo