Todos os artigos
129 artigos · atualizado semanalmente Veja nossas Ferramentas
Todos os artigos
Tutoriais

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.

Como converter Base64 em texto, imagem e arquivo
COVER · Tutoriais

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 \n e 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.

RD
Autor
Rafael Duarte
Desenvolvedor backend com passagem por fintech e SaaS B2B — trabalhou em times que escalaram APIs de zero a milhões de requisições. Carrega cicatrizes de produção suficientes para ter opiniões fortes sobre ferramentas, padrões e decisões de arquitetura. Não é acadêmico: leu a RFC do UUID quando precisou escolher entre v4 e v7 para uma tabela de alta escrita.
Ver perfil