Um cliente navegador e um serviço Node.js devem produzir o mesmo Base64 para os mesmos bytes. Eles não precisam usar o mesmo API para fazer isso. O acordo importante é a representação de bytes do texto, o alfabeto e o tratamento do preenchimento e da entrada inválida.
Navegador: torne a etapa UTF-8 explícita
As funções tradicionais do navegador operam em cadeias de bytes. Converta texto Unicode com TextEncodere codifique esses bytes. No caminho de retorno, reconstrua uma matriz de bytes e decodifique UTF-8 explicitamente. Este exemplo usa um loop para evitar a expansão de uma matriz grande em argumentos de função, embora um codificador de arquivo de produção ainda deva usar pedaços limitados ou um byte apropriado API.
const text = '中文😊';
const bytes = new TextEncoder().encode(text);
let binary = '';
for (const byte of bytes) binary += String.fromCharCode(byte);
const encoded = btoa(binary);
console.log(encoded); // 5Lit5paH8J+Yig==
const decodedBytes = Uint8Array.from(atob(encoded), c => c.charCodeAt(0));
const decoded = new TextDecoder('utf-8', { fatal: true }).decode(decodedBytes);
console.log(decoded); // 中文😊
Node.js: mantenha Buffer no limite de bytes
Buffer.from(text, 'utf8') torna a codificação do texto visível. Chamando toString('base64') então representa esses bytes. A decodificação para Buffer preserva os dados binários; a conversão imediata para uma string UTF-8 só é apropriada quando a carga útil é conhecida como texto.
import { Buffer } from 'node:buffer';
const encoded = Buffer.from('中文😊', 'utf8').toString('base64');
console.log(encoded); // 5Lit5paH8J+Yig==
const bytes = Buffer.from(encoded, 'base64');
console.log(new TextDecoder('utf-8', { fatal: true }).decode(bytes)); // 中文😊
O fatal TextDecoder no exemplo é deliberado. A conversão de string Buffer UTF-8 comum pode inserir caracteres de substituição para entrada inválida. Uma exibição de texto bem-sucedida ocultaria então que os bytes originais não poderiam ser representados fielmente.
Concordar com a validação separadamente
O decodificador Base64 do nó aceita alguns formulários de entrada que uma aplicação mais rigorosa pode rejeitar. Não use uma conversão Buffer bem-sucedida como prova de que a representação canônica de um protocolo foi fornecida. Se a sintaxe exata for importante para assinaturas, chaves de cache ou serialização interoperável, verifique-a antes de decodificar.
Para um teste de regressão compartilhado, inclua ASCII, chinês, emoji, byte zero, byte FF e todos os comprimentos em torno de um limite de três bytes. Compare bytes em tempos de execução. Teste também a saída segura de URL com um fixture que realmente contém os caracteres substituídos; uma entrada cuja saída contém apenas letras não pode provar que a opção variante funciona.
Ao ler arquivos no Node, mantenha o Buffer retornado em vez de aplicar uma codificação de texto ao arquivo lido. No navegador, use File.arrayBuffer(). Essas pequenas escolhas de limites evitam a maior parte da corrupção acidental. Veja Node.js Buffer para comportamento do nó e MDN: btoa() para o contrato de sequência de bytes do navegador.