Base64 em JavaScript: codificar e decodificar sem quebrar acentos
JavaScript tem duas formas de trabalhar com Base64, e a mais conhecida — btoa e atob — quebra com qualquer caractere fora do Latin-1. Este guia mostra o caminho correto no navegador e no Node.
O problema do btoa com acentos
btoa espera uma string em que cada caractere caiba em um byte. Qualquer coisa acima do código 255 — um ç, um emoji, um caractere cirílico — lança InvalidCharacterError. Como boa parte do conteúdo em português tem acento, o erro aparece cedo.
A solução é converter o texto para bytes UTF-8 antes de codificar, e desfazer o caminho na volta.
// ✗ quebra com acento
btoa("ação"); // InvalidCharacterError
// ✓ navegador: passa por UTF-8 antes
function paraBase64(texto) {
const bytes = new TextEncoder().encode(texto);
const binario = Array.from(bytes, (b) => String.fromCharCode(b)).join("");
return btoa(binario);
}
function deBase64(base64) {
const binario = atob(base64);
const bytes = Uint8Array.from(binario, (c) => c.charCodeAt(0));
return new TextDecoder().decode(bytes);
}
paraBase64("ação"); // "YcOnw6Nv"
deBase64("YcOnw6Nv"); // "ação"No Node.js
No Node o Buffer resolve os dois sentidos e já trata UTF-8 por padrão, sem a conversão manual.
const codificado = Buffer.from("ação", "utf8").toString("base64");
// "YcOnw6Nv"
const decodificado = Buffer.from(codificado, "base64").toString("utf8");
// "ação"Base64 seguro para URL
O Base64 padrão usa +, / e = — três caracteres que precisam de escape em URL. A variante base64url troca os dois primeiros e descarta o preenchimento. É o formato usado em JWT.
const paraUrl = (base64) =>
base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
const deUrl = (seguro) => {
const base = seguro.replace(/-/g, "+").replace(/_/g, "/");
return base + "=".repeat((4 - (base.length % 4)) % 4);
};
// No Node, direto:
Buffer.from("ação").toString("base64url");Arquivos e imagens
Para arquivos, o FileReader devolve um data URI já em Base64. O prefixo antes da vírgula descreve o tipo — separe se você quer só o conteúdo.
const leitor = new FileReader();
leitor.onload = () => {
const dataUri = leitor.result; // "data:image/png;base64,iVBORw0..."
const apenasBase64 = dataUri.split(",")[1]; // "iVBORw0..."
};
leitor.readAsDataURL(arquivo);Perguntas frequentes
- Por que btoa dá InvalidCharacterError?
- Porque btoa só aceita caracteres de 0 a 255. Qualquer acento, emoji ou caractere não latino ultrapassa esse limite. Converta o texto para bytes UTF-8 com TextEncoder antes de chamar btoa.
- Base64 criptografa o conteúdo?
- Não. É apenas uma representação em texto de dados binários, reversível por qualquer pessoa. Nunca use Base64 para proteger senha, token ou dado pessoal.