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ável | Alfabeto | Preenchimento | <<???>> vira |
|---|---|---|---|
StdEncoding | + / | sim | PDw/Pz8+Pg== |
URLEncoding | - _ | sim | PDw_Pz8-Pg== |
RawStdEncoding | + / | não | PDw/Pz8+Pg |
RawURLEncoding | - _ | não | PDw_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:
| Entrada | Erro |
|---|---|
PDw_Pz8-Pg== | illegal base64 data at input byte 3 (o _) |
aGk | illegal base64 data at input byte 0 |
aGk=!! | illegal base64 data at input byte 4 |
aGk== | illegal base64 data at input byte 4 |
aGVs bG8K | illegal 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. UseRawURLEncodingou inspecione o token antes no decodificador de JWT. - Ignorar o resultado parcial.
"aGk=!!"retorna"hi"e um erro. Não use os bytes quandoerr != nil. - Esquecer o
Close()do codificador. O último bloco se perde. - Espaços vindos de copiar e colar. O decodificador só pula
\re\n. Remova os demais espaços em branco antes comstrings.Join(strings.Fields(s), "").
Notas de versão
RawStdEncodingeRawURLEncoding: 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
mimeenet/mailcuidam de e-mail, ou insira\r\na 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.