Enviar um arquivo
  1. base64hub
  2. Base64 no código
  3. Go (Golang)
Código

Base64 em Go

O pacote encoding/base64 do Go traz quatro codificações prontas e wrappers para streaming. Escolher a certa entre as quatro é quase todo o trabalho. As saídas abaixo vêm do Go 1.25.

Codificar e decodificar uma string

As strings do Go já são UTF-8, então basta converter para []byte:

package main

import (
	"encoding/base64"
	"fmt"
)

func main() {
	enc := base64.StdEncoding.EncodeToString([]byte("héllo wörld"))
	fmt.Println(enc) // aMOpbGxvIHfDtnJsZA==

	dec, err := base64.StdEncoding.DecodeString(enc)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(dec)) // héllo wörld
}

Sempre verifique o err. Quando falha, o DecodeString pode retornar os bytes decodificados até ali junto com o erro, então usar o resultado sem a verificação entrega dados truncados em silêncio.

As quatro codificações: Std, URL, Raw

VariávelAlfabetoPreenchimento<<???>> vira
StdEncoding+ /simPDw/Pz8+Pg==
URLEncoding- _simPDw_Pz8-Pg==
RawStdEncoding+ /nãoPDw/Pz8+Pg
RawURLEncoding- _nãoPDw_Pz8-Pg
d := []byte("<<???>>")
base64.URLEncoding.EncodeToString(d)    // PDw_Pz8-Pg==
base64.RawURLEncoding.EncodeToString(d) // PDw_Pz8-Pg

b, err := base64.RawURLEncoding.DecodeString("PDw_Pz8-Pg") // "<<???>>", nil

A decodificação é rígida com o preenchimento nas duas direções. As codificações com preenchimento rejeitam entrada sem ele, e as raw rejeitam =:

base64.StdEncoding.DecodeString("aGk")
// illegal base64 data at input byte 0
base64.RawURLEncoding.DecodeString("PDw_Pz8-Pg==")
// illegal base64 data at input byte 10

Se você aceita entrada de várias origens, remova os = do final com strings.TrimRight(s, "=") e decodifique com uma codificação Raw. Também dá para criar codificações próprias com base64.NewEncoding(alphabet) e .WithPadding(base64.NoPadding). A página Base64 vs Base64URL explica onde cada variante aparece.

Codificar e decodificar um arquivo

data, err := os.ReadFile("hello.txt")
if err != nil {
	log.Fatal(err)
}
fmt.Println(base64.StdEncoding.EncodeToString(data)) // aGVsbG8K

raw, err := base64.StdEncoding.DecodeString("aGVsbG8K")
if err != nil {
	log.Fatal(err)
}
os.WriteFile("copy.txt", raw, 0o644)

Streaming com NewEncoder e NewDecoder

Para arquivos grandes, envolva um io.Writer ou um io.Reader. O uso de memória fica constante:

in, _ := os.Open("big.bin")
defer in.Close()
out, _ := os.Create("big.b64")
defer out.Close()

w := base64.NewEncoder(base64.StdEncoding, out)
if _, err := io.Copy(w, in); err != nil {
	log.Fatal(err)
}
if err := w.Close(); err != nil { // flushes the last partial block
	log.Fatal(err)
}

O w.Close() não fecha o out; ele escreve o último ou os dois últimos bytes e o preenchimento. Esquecê-lo é o bug clássico de Base64 em Go: a saída parece correta, mas fica até quatro caracteres mais curta. A decodificação é simétrica:

r := base64.NewDecoder(base64.StdEncoding, f)
io.Copy(dst, r)

Com um arquivo de teste de 300 KB, os resultados em streaming e em memória foram idênticos. O decodificador pula \r e \n, então lê entrada com quebras MIME diretamente. Para calcular o tamanho dos buffers com antecedência, EncodedLen(5) retorna 8 e RawStdEncoding.EncodedLen(5) retorna 7; o guia de aumento de tamanho mostra a fórmula por trás.

O Go 1.22 acrescentou AppendEncode e AppendDecode, que anexam a um slice existente. Útil para headers:

dst := base64.StdEncoding.AppendEncode([]byte("Basic "), []byte("user:pass"))
fmt.Println(string(dst)) // Basic dXNlcjpwYXNz

O gerador de header Basic Auth monta o mesmo valor no navegador.

Validação e CorruptInputError

Todo erro de decodificação é um base64.CorruptInputError, um int64 com a posição do primeiro byte inválido:

_, err := base64.StdEncoding.DecodeString("PDw_Pz8-Pg==")
var ce base64.CorruptInputError
if errors.As(err, &ce) {
	fmt.Println(err) // illegal base64 data at input byte 3
}

Resultados que obtivemos com StdEncoding:

EntradaErro
PDw_Pz8-Pg==illegal base64 data at input byte 3 (o _)
aGkillegal base64 data at input byte 0
aGk=!!illegal base64 data at input byte 4
aGk==illegal base64 data at input byte 4
aGVs bG8Killegal base64 data at input byte 4 (espaço)
aGl=sem erro, retorna hi
"aGVs\nbG8K"sem erro, a quebra de linha é pulada

aGl= tem bits de preenchimento diferentes de zero. Para rejeitar entrada não canônica como essa, use .Strict():

_, err = base64.StdEncoding.Strict().DecodeString("aGl=")
// illegal base64 data at input byte 3

O validador de Base64 destaca as mesmas posições; erros comuns de Base64 reúne as causas habituais.

Armadilhas

  • Decodificar partes de JWT com StdEncoding. Tokens são Base64URL sem preenchimento. Use RawURLEncoding ou inspecione o token antes no decodificador de JWT.
  • Ignorar o resultado parcial. "aGk=!!" retorna "hi" e um erro. Não use os bytes quando err != nil.
  • Esquecer o Close() do codificador. O último bloco se perde.
  • Espaços vindos de copiar e colar. O decodificador só pula \r e \n. Remova os demais espaços em branco antes com strings.Join(strings.Fields(s), "").

Notas de versão

  • RawStdEncoding e RawURLEncoding: Go 1.5.
  • Strict(): Go 1.8.
  • AppendEncode, AppendDecode: Go 1.22.
  • O pacote não tem codificador com quebra de linha MIME; os pacotes mime e net/mail cuidam de e-mail, ou insira \r\n a cada 76 caracteres você mesmo.

Perguntas frequentes

Qual é a diferença entre URLEncoding e RawURLEncoding?

As duas usam o alfabeto URL-safe, com - e _. A URLEncoding escreve o preenchimento = e o exige ao decodificar; a RawURLEncoding omite o preenchimento e o rejeita. JWTs usam RawURLEncoding.

O que significa 'illegal base64 data at input byte 0'?

É um base64.CorruptInputError, e o número é a posição em que a decodificação falhou. No byte 0 com uma string curta, geralmente indica comprimento errado, muitas vezes por falta de preenchimento; use a codificação Raw para entrada sem preenchimento.

O decodificador Base64 do Go ignora quebras de linha?

Sim. As quatro codificações pulam \r e \n, então entrada com quebras MIME decodifica. Espaços e outros caracteres são erros.

Continue lendo