Enviar um arquivo
  1. base64hub
  2. Aprenda Base64
  3. Uint8Array.toBase64()
Guia

Uint8Array.toBase64 e fromBase64 no JavaScript

O JavaScript enfim tem uma API Base64 que trabalha com bytes, lida com Base64URL e permite escolher o quão rígida é a decodificação. Ela substitui a maior parte dos usos de btoa, atob e loops escritos à mão.

Codificar e decodificar numa linha

A API acrescenta três métodos para Base64 e três para hex:

MétodoO que faz
bytes.toBase64(options)Uint8Array para uma string Base64
Uint8Array.fromBase64(string, options)String Base64 para um novo Uint8Array
bytes.setFromBase64(string, options)Decodifica para um Uint8Array existente
bytes.toHex()Uint8Array para uma string hex em minúsculas
Uint8Array.fromHex(string)String hex para um novo Uint8Array
bytes.setFromHex(string)Decodifica hex para um Uint8Array existente

O texto precisa virar bytes primeiro. O TextEncoder sempre produz UTF-8, então Unicode funciona sem truques:

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, 世界"

Um ArrayBuffer, por exemplo vindo de fetch().arrayBuffer() ou crypto.subtle.digest(), precisa de uma view: new Uint8Array(buffer).toBase64(). Os outros typed arrays não têm os métodos. Para codificar a memória bruta de um Uint16Array, envolva-a: new Uint8Array(u16.buffer, u16.byteOffset, u16.byteLength).

Opções: alphabet, omitPadding, lastChunkHandling

alphabet

"base64" (o padrão) usa + e /. "base64url" usa - e _. A opção vale tanto para codificar quanto para decodificar, e um decodificador configurado para um alfabeto rejeita os caracteres do outro.

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

O toBase64 acrescenta o preenchimento = por padrão, mesmo com o alfabeto Base64URL. Passe omitPadding: true para obter saída no estilo JWT. Na decodificação, o preenchimento é controlado pelo lastChunkHandling.

lastChunkHandling

Esta opção decide o que fromBase64 e setFromBase64 fazem com o último grupo de caracteres. Os três modos, testados com uma entrada que decodifica para os bytes de Hi?>:

Entrada"loose" (padrão)"strict""stop-before-partial"
SGk/Pg==Hi?>Hi?>Hi?>
SGk/Pg (sem preenchimento)Hi?>SyntaxErrorHi? (para antes de Pg)
SGk/Ph== (bits de enchimento diferentes de zero)Hi?>SyntaxErrorHi?>
SGk/P (um caractere sobrando)SyntaxErrorSyntaxErrorHi?
  • loose se comporta como o atob: o preenchimento é opcional, e os bits que sobram no último caractere são ignorados.
  • strict aceita apenas entrada canônica e com preenchimento, como a RFC 4648 descreve. Use quando a forma codificada precisa ser única, por exemplo quando strings Base64 são comparadas ou assinadas.
  • stop-before-partial decodifica todos os grupos completos de 4 caracteres e deixa de lado um final incompleto. Isso é feito para streaming, em que o resto do grupo chega no próximo pedaço.

Em todos os modos, os espaços em branco ASCII (espaços, tabulações, quebras de linha) são ignorados, então entrada quebrada no estilo MIME decodifica direto. Qualquer outro caractere fora do alfabeto lança um SyntaxError.

Escrevendo num buffer existente

setFromBase64 e setFromHex decodificam para um Uint8Array que você já tem e retornam quanto usaram:

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

Hello world tem 11 bytes, e o destino comporta 8. O decodificador só escreve grupos inteiros de 3 bytes que cabem, então para depois de 6 bytes e informa que leu 8 caracteres. Chame de novo com o resto da string e um subarray do destino para continuar. Combinado com stop-before-partial, isso permite decodificar um stream pedaço a pedaço para um buffer pré-alocado:

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.

Os métodos hex seguem o mesmo padrão. O toHex() sempre escreve em minúsculas, o fromHex() aceita as duas caixas, e um número ímpar de dígitos gera SyntaxError:

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

Por que btoa e atob são desajeitados

btoa e atob vêm dos primórdios dos navegadores e trabalham com “strings binárias”: strings JavaScript em que cada código de caractere é um valor de byte de 0 a 255. Isso causa três problemas.

Unicode ou lança erro ou dá errado em silêncio. Caracteres acima de U+00FF fazem o btoa lançar um InvalidCharacterError (no Chrome: “The string to be encoded contains characters outside of the Latin1 range”). Caracteres entre U+0080 e U+00FF são piores: são codificados sem erro, como Latin-1. btoa("Grüße") retorna R3L832U=, enquanto a codificação UTF-8 que todos os outros sistemas esperam é R3LDvMOfZQ==.

A decodificação retorna uma string, não bytes. atob("Q2Fmw6k=") retorna "Café". Para obter o texto de verdade, você converte a string em bytes e depois os decodifica como UTF-8, o que exige um loop ou Uint8Array.from(atob(s), c => c.charCodeAt(0)).

Entradas grandes pedem cuidado. O one-liner comum btoa(String.fromCharCode(...bytes)) passa cada byte como argumento de função, e com um array de 1 MB ele lança RangeError: Maximum call stack size exceeded. É preciso montar a string em pedaços.

Também não há suporte a Base64URL nem controle sobre preenchimento ou rigidez. O velho contorno btoa(unescape(encodeURIComponent(text))) até produz Base64 de UTF-8, mas depende do unescape, que está obsoleto. Com a nova API, tudo isso vira new TextEncoder().encode(text).toBase64().

Suporte em navegadores e Node.js

Os métodos vêm da proposta do TC39 “Uint8Array to/from Base64”, que chegou ao Stage 4 em julho de 2025 e faz parte do ECMAScript 2026.

RuntimePrimeira versão com os seis métodos
Chrome, Edge140
Firefox133
Safari (macOS e iOS)18.2
Node.js25.0.0

Com o Chrome 140, o recurso virou Baseline em setembro de 2025, então todos os principais navegadores atuais o têm. No Node.js, testamos os métodos diretamente. O Node 25.0.0 e o 26.12.1 têm os seis. O Node 24.22.1, uma versão LTS, não tem nenhum deles por padrão. O V8 dele aceita uma flag experimental --js-base-64, mas no nosso teste essa flag expôs toBase64, fromBase64, toHex e fromHex sem setFromBase64 nem setFromHex, então trate o Node 24 como sem suporte. No Node 24 e anteriores, use Buffer.from(b64, "base64") e buf.toString("base64"), que também aceitam "base64url". O Deno 2.5 e o Bun 1.1.22 também suportam a API, segundo os dados de compatibilidade do MDN.

Um fallback para runtimes mais antigos

Detecte o recurso e recorra a btoa/atob com processamento em pedaços. Esta versão foi testada no Node 24 (caminho do fallback) e no Node 26 (caminho nativo) com texto UTF-8 e um array de 1 MB:

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

O fallback cobre só as opções padrão. Se você precisa de base64url, strict ou setFromBase64 em todo lugar, use um polyfill completo: o core-js traz um em es.uint8-array.to-base64, es.uint8-array.from-base64 e módulos relacionados.

Para testar codificações sem escrever código, use o codificador Base64 e o decodificador, ou o Base64URL para o alfabeto URL-safe. Mais exemplos em JavaScript, incluindo o Buffer do Node, estão em Base64 em JavaScript e Base64 em Node.js.

Perguntas frequentes

A nova API Base64 já pode ir para produção?

Nos navegadores, sim: ela está no Chrome e no Edge 140, no Firefox 133 e no Safari 18.2. No Node.js é preciso a versão 25 ou posterior. Mantenha um fallback se você dá suporte a navegadores mais antigos ou ao Node 24 e anteriores.

O Uint8Array.fromBase64 aceita Base64URL?

Sim, com { alphabet: "base64url" }. Sem essa opção, - e _ causam um SyntaxError. O preenchimento é opcional no modo tolerante padrão, para os dois alfabetos.

Como codificar uma string com toBase64?

Converta-a antes em bytes UTF-8: new TextEncoder().encode(text).toBase64(). Para decodificar, faça o caminho inverso com new TextDecoder().decode(Uint8Array.fromBase64(b64)).

Por que toBase64 não é uma função no meu Buffer ou Uint16Array?

Os métodos existem só no Uint8Array e nas subclasses dele. O Buffer do Node os ganha a partir do Node 25. Para outros typed arrays, envolva a memória: new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength).

Continue lendo