Subir un archivo
  1. base64hub
  2. Base64 en código
  3. Go (Golang)
Código

Base64 en Go

El paquete encoding/base64 de Go trae cuatro codificaciones listas para usar y wrappers para trabajar en flujo. Elegir la correcta de las cuatro es casi todo el trabajo. Las salidas de esta página son de Go 1.25.

Codificar y decodificar una cadena

Las cadenas de Go ya son UTF-8, así que basta con convertirlas a []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
}

Comprueba siempre err. Cuando falla, DecodeString puede devolver los bytes decodificados hasta ese punto junto con el error, así que usar el resultado sin comprobarlo te da datos truncados sin avisar.

Las cuatro codificaciones: Std, URL, Raw

VariableAlfabetoRelleno<<???>> se codifica como
StdEncoding+ /síPDw/Pz8+Pg==
URLEncoding- _síPDw_Pz8-Pg==
RawStdEncoding+ /noPDw/Pz8+Pg
RawURLEncoding- _noPDw_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

La decodificación es estricta con el relleno en ambos sentidos. Las codificaciones con relleno rechazan las entradas sin él, y las Raw rechazan el =:

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

Si aceptas entradas de varias fuentes, quita los = finales con strings.TrimRight(s, "=") y decodifica con una codificación Raw. También puedes crear codificaciones propias con base64.NewEncoding(alphabet) y .WithPadding(base64.NoPadding). La página Base64 frente a Base64URL explica dónde aparece cada variante.

Codificar y decodificar un archivo

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)

Flujos con NewEncoder y NewDecoder

Con archivos grandes, envuelve un io.Writer o un io.Reader. El uso de memoria se mantiene 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)
}

w.Close() no cierra out: escribe los últimos uno o dos bytes y el relleno. Olvidarlo es el error clásico de Base64 en Go: la salida parece correcta, pero le faltan hasta cuatro caracteres. La decodificación es simétrica:

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

Con un archivo de prueba de 300 KB, los resultados en flujo y en memoria fueron idénticos. El decodificador se salta \r y \n, así que lee directamente entradas con cortes MIME. Para calcular de antemano el tamaño de los búferes, EncodedLen(5) devuelve 8 y RawStdEncoding.EncodedLen(5) devuelve 7; la guía del aumento de tamaño tiene la fórmula que hay detrás.

Go 1.22 añadió AppendEncode y AppendDecode, que añaden el resultado a un slice existente. Resultan prácticos para cabeceras:

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

El generador de cabeceras Basic Auth construye el mismo valor en el navegador.

Validación y CorruptInputError

Todos los errores de decodificación son base64.CorruptInputError, un int64 con la posición del primer byte incorrecto:

_, 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 obtuvimos con StdEncoding:

EntradaError
PDw_Pz8-Pg==illegal base64 data at input byte 3 (el _)
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 (espacio)
aGl=sin error, devuelve hi
"aGVs\nbG8K"sin error, se salta el salto de línea

aGl= tiene bits de relleno distintos de cero. Para rechazar entradas no canónicas como esa, usa .Strict():

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

El validador Base64 marca las mismas posiciones; en errores habituales de Base64 están las causas más frecuentes.

Trampas

  • Decodificar las partes de un JWT con StdEncoding. Los tokens son Base64URL sin relleno. Usa RawURLEncoding, o inspecciona antes el token con el decodificador JWT.
  • Pasar por alto el resultado parcial. "aGk=!!" devuelve "hi" y un error. No uses los bytes cuando err != nil.
  • Olvidar Close() en el codificador. Se pierde el último bloque.
  • Espacios al copiar y pegar. El decodificador solo se salta \r y \n. Elimina antes el resto de espacios en blanco con strings.Join(strings.Fields(s), "").

Notas de versión

  • RawStdEncoding y RawURLEncoding: Go 1.5.
  • Strict(): Go 1.8.
  • AppendEncode, AppendDecode: Go 1.22.
  • El paquete no tiene un codificador con cortes de línea MIME; los paquetes mime y net/mail se ocupan del correo, o puedes insertar tú mismo \r\n cada 76 caracteres.

Preguntas frecuentes

¿Qué diferencia hay entre URLEncoding y RawURLEncoding?

Las dos usan el alfabeto URL-safe con - y _. URLEncoding escribe el relleno = y lo exige al decodificar; RawURLEncoding lo omite y lo rechaza. Los JWT usan RawURLEncoding.

¿Qué significa «illegal base64 data at input byte 0»?

Es un base64.CorruptInputError, y el número es la posición en la que falló la decodificación. En el byte 0 y con una cadena corta suele indicar una longitud incorrecta, a menudo por falta de relleno; usa la codificación Raw para entradas sin relleno.

¿El decodificador Base64 de Go ignora los saltos de línea?

Sí. Las cuatro codificaciones se saltan \r y \n, así que las entradas con cortes MIME se decodifican. Los espacios y otros caracteres son errores.

Sigue leyendo