Como converter Base64 em texto, imagem e arquivo
Como converter Base64 de volta em texto, imagem e arquivo no browser e na linha de comando, com o UTF-8 gotcha, o prefixo Data URI e os erros comuns.
Você abre o JSON que veio de uma API e no meio do payload tem um campo gigante começando com iVBORw0KGgo. Ou recebe um e-mail com anexo e o servidor cuspiu um bloco de texto sem fim no header. Ou pega um data:image/png;base64,... no CSS de alguém e quer só ver que imagem é aquela sem abrir o navegador. Em todos esses casos a pergunta é a mesma: como destrincho esse Base64 de volta para algo que faça sentido — texto, imagem ou arquivo?
Este guia é a parte prática. Vou mostrar os três casos que aparecem no dia a dia (string para texto, Base64 para imagem, arquivo para Base64 e de volta), como fazer no browser, na linha de comando, e os erros que fazem o decoder estourar na sua cara. Se você ainda não sabe o que é Base64 ou por que ele não é criptografia, leia antes o que é Base64 e por que ele aparece em todo lugar — aqui assumo que o conceito já está claro.
Caso 1: string Base64 de volta para texto
O caso mais simples. Você tem b2xhIG11bmRv e quer o texto original. No browser, o par é btoa (encode) e atob (decode):
btoa("ola mundo") // "b2xhIG11bmRv"
atob("b2xhIG11bmRv") // "ola mundo"
Funciona — até alguém digitar um acento. btoa("ação") lança InvalidCharacterError, porque btoa só aceita caracteres no range Latin-1 (0–255), e "ç" e "ã" em JavaScript são code points Unicode acima disso. Esse é o famoso UTF-8 gotcha. A correção é converter a string para bytes UTF-8 antes de codificar:
// encode seguro p/ acentos e emoji
const b64 = btoa(String.fromCharCode(...new TextEncoder().encode("ação 🚀")));
// decode de volta
const txt = new TextDecoder().decode(
Uint8Array.from(atob(b64), c => c.charCodeAt(0))
);
TextEncoder transforma o texto em bytes UTF-8 reais; btoa então codifica esses bytes. Na volta, atob devolve os bytes e TextDecoder os remonta como UTF-8. Pule esse passo e você vai ver "ção" no lugar de "ação" — o clássico mojibake.
Na linha de comando o problema some, porque ferramentas Unix trabalham com bytes desde sempre:
echo -n "ação" | base64 # decodifica corretamente acentos
echo "YcOnw6Nv" | base64 -d
O -n no echo importa: sem ele você codifica um \n extra no fim e o resultado não bate com o do browser.
Caso 2: Base64 para imagem (e o prefixo Data URI)
Aqui mora a confusão mais comum. Uma string Base64 pura — só os caracteres A–Z a–z 0–9 + / — não é uma imagem. É a representação dos bytes do arquivo. Para o browser tratar isso como imagem, você precisa do prefixo Data URI que diz o tipo:
data:image/png;base64,iVBORw0KGgoAAAANS...
Esse data:image/png;base64, não é decoração. Ele informa ao browser o MIME type (image/png) e o esquema de codificação (base64). Cole isso no src de uma <img> e a imagem aparece:
<img src="data:image/png;base64,iVBORw0KGgoAAAANS..." alt="">
Erre o MIME type — image/jpeg num PNG — e dependendo do browser a imagem nem renderiza. Se você só tem o blob Base64 sem o prefixo, precisa saber o formato original para remontar o Data URI corretamente. Dica: PNG sempre começa em iVBOR, JPEG em /9j/, GIF em R0lGOD. Esses são os primeiros bytes do header de cada formato codificados.
Para reconstruir um arquivo de imagem real a partir do Base64 puro na linha de comando:
# tira o prefixo data:... antes, se houver
base64 -d imagem.b64 > imagem.png
No browser, para baixar a imagem decodificada:
const bytes = Uint8Array.from(atob(b64Puro), c => c.charCodeAt(0));
const blob = new Blob([bytes], { type: "image/png" });
const url = URL.createObjectURL(blob);
// usa url no href de um <a download> ou no src de uma <img>
Note que aqui eu uso o mesmo truque do Uint8Array.from(atob(...)) do caso de texto — porque imagem é só bytes binários, não texto. Tratar bytes binários como string e jogar num TextDecoder corromperia o arquivo. Se você quer só fazer a ida e volta sem escrever código, dá para colar a string e ver o resultado direto no conversor Base64 do Quick Tools, que detecta o tipo e mostra a imagem.
Caso 3: arquivo qualquer para Base64 e de volta
PDF, ZIP, fonte, áudio — qualquer arquivo binário vira Base64 da mesma forma, porque para o codificador tudo é uma sequência de bytes. O uso típico: embutir um arquivo dentro de um JSON ou de um campo de banco que só aceita texto.
Linha de comando, ida e volta:
base64 documento.pdf > documento.b64 # encode
base64 -d documento.b64 > documento.pdf # decode
Para arquivos grandes, lembre que Base64 infla o tamanho em ~33% — três bytes viram quatro caracteres. Mandar um vídeo de 50MB como Base64 num payload JSON é pedir timeout. Use Base64 para coisas pequenas (ícones, certificados, thumbnails); para arquivos grandes, suba o binário e mande só a URL.
Com openssl o resultado é o mesmo, útil quando você já está no contexto de chaves e certificados:
openssl base64 -in chave.pem -out chave.b64
openssl base64 -d -in chave.b64 -out chave.pem
Uma diferença prática: openssl base64 quebra a saída em linhas de 64 caracteres por padrão, enquanto muitos parsers esperam uma linha só. Se o decoder reclamar, remova os newlines antes.
Os erros que fazem o decoder estourar
Quatro armadilhas respondem por quase todo "por que isso não decodifica":
- Padding errado ou faltando. Base64 usa
=no fim para completar o último grupo. Comprimento que não é múltiplo de 4 ou=removido por uma sanitização zelosa derruba decoders estritos. Se a string parece truncada no fim, é quase sempre isso. - Variante URL-safe. Quando o Base64 viaja numa URL ou num JWT,
+vira-e/vira_. Um decoder padrão não reconhece-e_e falha. Converta de volta (-→+,_→/) antes de decodificar, ou use a função específica de URL-safe. - Espaços e quebras de linha coladas junto. Copiar de um e-mail ou de um terminal arrasta
\ne espaços. A maioria dos decoders tolera, alguns não. No susto, limpe whitespace primeiro. - Achar que é criptografia. Base64 é codificação, não segurança. Qualquer um decodifica em um segundo. Nunca, jamais, "esconda" senha ou token só botando em Base64 — isso é o equivalente digital de esconder a chave embaixo do tapete.
Perguntas frequentes
Como sei se uma string Base64 é texto, imagem ou arquivo?
Pelos primeiros caracteres do conteúdo decodificado, que correspondem ao header do formato. iVBOR é PNG, /9j/ é JPEG, JVBERi é PDF, UEsDB é ZIP/Office. Se decodificar e vier texto legível, era texto. Um conversor decente detecta isso automaticamente e mostra o preview certo.
Por que meus acentos viram caracteres estranhos depois de decodificar?
Porque a codificação não passou pelo UTF-8. btoa/atob do browser trabalham byte a byte no range Latin-1; se você não converte com TextEncoder/TextDecoder, qualquer caractere acima de 255 (acentos, emoji) corrompe. Use o par TextEncoder no encode e TextDecoder no decode. Na linha de comando o base64 já lida com bytes UTF-8 nativamente.
Preciso mesmo do prefixo data:image/png;base64,?
Para o browser renderizar via src de <img> ou no CSS, sim — é o que diz qual o tipo do conteúdo. Para reconstruir o arquivo no disco com base64 -d, não: ali você decodifica só os bytes e o prefixo, se estiver presente, precisa ser removido antes ou o arquivo sai corrompido.
Base64 aumenta o tamanho do arquivo?
Sim, em torno de 33%. Cada três bytes binários viram quatro caracteres ASCII. Por isso Base64 só faz sentido para coisas pequenas embutidas em texto; para arquivos grandes o overhead de tamanho e parsing não compensa.
O que levar deste guia
Decodificar Base64 é sempre o mesmo movimento: separar o prefixo (se houver), reconhecer a variante (padrão ou URL-safe), e tratar o conteúdo como bytes — não como texto — quando for imagem ou arquivo. O UTF-8 gotcha do btoa/atob e o padding faltando são responsáveis por 90% das dores de cabeça; resolva esses dois e o resto é mecânico. E lembre que Base64 nunca foi e nunca será segurança: é transporte de bytes em forma de texto, ponto.
- 01 O que é software open source Open source não significa gratuito. Entenda o que a licença realmente define, as diferenças entre MIT, GPL e Apache, e como projetos open source se sustentam financeiramente.
- 02 PostgreSQL vs MySQL: diferenças práticas para quem precisa escolher Comparativo honesto entre PostgreSQL e MySQL: tipos nativos, JSONB vs JSON_EXTRACT, concorrência MVCC e extensões. Por que PostgreSQL como default faz sentido.