Quando Base64 parece “quebrar o chinês”, o problema geralmente é o limite entre caracteres e bytes. Base64 não sabe que uma sequência representa texto ou emoji chinês. Preserva bytes; o aplicativo escolhe como esses bytes são produzidos e interpretados.
Siga os bytes
O caractere 中 é U+4E2D. Em UTF-8 utiliza os bytes E4 B8 AD, cuja representação Base64 é 5Lit. Uma codificação de caracteres diferente produz bytes diferentes e, portanto, um valor Base64 diferente. Concorde com UTF-8 em ambas as extremidades antes de comparar as strings codificadas.
const bytes = new TextEncoder().encode('中');
console.log([...bytes]); // [228, 184, 173]
console.log([...bytes].map(b => b.toString(16)).join(' ')); // e4 b8 ad
Emoji ilustra outra distinção: JavaScript '😊'.length tem duas unidades de código UTF-16, enquanto sua representação UTF-8 tem quatro bytes. Nenhum dos números sozinho é uma contagem de caracteres visíveis percebidos pelo usuário em texto arbitrário. Use comprimento de bytes para cálculos de arquivo e transporte.
Tornar a substituição visível
O caractere de substituição � pode significar que um decodificador anterior já perdeu informações. Depois que bytes desconhecidos forem substituídos em uma string e essa string for recodificada, os valores originais geralmente não poderão ser reconstruídos. Inspecione a matriz de bytes decodificados brutos antes de fazer conversões adicionais.
const decoder = new TextDecoder('utf-8', { fatal: true });
console.log(decoder.decode(Uint8Array.of(0xe4, 0xb8, 0xad))); // 中
try { decoder.decode(Uint8Array.of(0xff)); }
catch { console.log('Invalid UTF-8; preserve the original byte.'); }
A normalização é uma operação separada
Duas strings podem parecer idênticas embora contenham sequências Unicode diferentes. A letra acentuada é pode ser um caractere pré-composto ou um e seguido por um acento combinado. UTF-8 e Base64 preservam corretamente essa diferença. Se seu aplicativo deseja texto normalizado, escolha explicitamente um formulário de normalização e aplique-o antes da codificação; nunca chame um resultado alterado de reparo com preservação de bytes.
Verifique também os caracteres invisíveis, os finais de linha e uma possível marca de ordem de bytes quando os resultados forem diferentes. Estes são bytes de entrada reais, não necessariamente bugs do codificador. Para mensagens assinadas e somas de verificação, mesmo uma diferença invisível altera o resultado.
Uma ordem confiável de solução de problemas é: preservar a fonte, inspecionar os bytes, identificar a codificação de caracteres esperada, decodificar com tratamento de erros explícito e só então considerar a normalização. MDN: btoa() explica por que as chamadas btoa diretas do navegador são inadequadas para texto Unicode arbitrário. Os arquivos binários devem ignorar totalmente a decodificação de texto.