Einen String kodieren und dekodieren
Go-Strings sind bereits UTF-8, die Umwandlung in []byte genügt also:
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
}
Prüfen Sie immer err. Im Fehlerfall kann DecodeString die bis dahin dekodierten Bytes zusammen mit dem Fehler zurückgeben; wer das Ergebnis ohne Prüfung verwendet, bekommt stillschweigend abgeschnittene Daten.
Die vier Kodierungen: Std, URL, Raw
| Variable | Alphabet | Padding | <<???>> wird zu |
|---|---|---|---|
StdEncoding | + / | ja | PDw/Pz8+Pg== |
URLEncoding | - _ | ja | PDw_Pz8-Pg== |
RawStdEncoding | + / | nein | PDw/Pz8+Pg |
RawURLEncoding | - _ | nein | 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
Beim Dekodieren ist Go in beide Richtungen streng, was das Padding angeht. Die Kodierungen mit Padding lehnen Eingaben ohne Padding ab, und die Raw-Varianten lehnen = ab:
base64.StdEncoding.DecodeString("aGk")
// illegal base64 data at input byte 0
base64.RawURLEncoding.DecodeString("PDw_Pz8-Pg==")
// illegal base64 data at input byte 10
Nehmen Sie Eingaben aus mehreren Quellen an, entfernen Sie abschließende = mit strings.TrimRight(s, "=") und dekodieren mit einer Raw-Kodierung. Eigene Kodierungen bauen Sie mit base64.NewEncoding(alphabet) und .WithPadding(base64.NoPadding). Die Seite Base64 vs. Base64URL erklärt, wo welche Variante vorkommt.
Eine Datei kodieren und dekodieren
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 mit NewEncoder und NewDecoder
Für große Dateien umhüllen Sie einen io.Writer oder io.Reader. Der Speicherverbrauch bleibt konstant:
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() schließt out nicht, sondern schreibt die letzten ein oder zwei Bytes und das Padding. Es wegzulassen ist der klassische Base64-Bug in Go: Die Ausgabe sieht gut aus, ist aber um bis zu vier Zeichen zu kurz. Das Dekodieren funktioniert symmetrisch:
r := base64.NewDecoder(base64.StdEncoding, f)
io.Copy(dst, r)
Gestreamte und im Speicher berechnete Ergebnisse waren bei einer 300-KB-Testdatei identisch. Der Decoder überspringt \r und \n und liest MIME-umbrochene Eingaben daher direkt. Zum Vorausberechnen von Puffergrößen liefert EncodedLen(5) den Wert 8 und RawStdEncoding.EncodedLen(5) den Wert 7; die Formel dahinter steht in der Anleitung zum Größenzuwachs.
Go 1.22 hat AppendEncode und AppendDecode eingeführt, die an einen vorhandenen Slice anhängen. Praktisch für Header:
dst := base64.StdEncoding.AppendEncode([]byte("Basic "), []byte("user:pass"))
fmt.Println(string(dst)) // Basic dXNlcjpwYXNz
Der Basic-Auth-Header-Generator baut denselben Wert im Browser.
Validierung und CorruptInputError
Jeder Dekodierfehler ist ein base64.CorruptInputError, ein int64-Offset des ersten ungültigen Bytes:
_, 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
}
Ergebnisse mit StdEncoding:
| Eingabe | Fehler |
|---|---|
PDw_Pz8-Pg== | illegal base64 data at input byte 3 (das _) |
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 (Leerzeichen) |
aGl= | kein Fehler, liefert hi |
"aGVs\nbG8K" | kein Fehler, Zeilenumbruch übersprungen |
aGl= hat Padding-Bits ungleich null. Um solche nicht kanonischen Eingaben abzulehnen, verwenden Sie .Strict():
_, err = base64.StdEncoding.Strict().DecodeString("aGl=")
// illegal base64 data at input byte 3
Der Base64-Validator markiert dieselben Positionen; unter Häufige Base64-Fehler stehen die üblichen Ursachen.
Fallstricke
- JWT-Teile mit
StdEncodingdekodieren. Tokens sind Base64URL ohne Padding. Verwenden SieRawURLEncodingoder sehen Sie sich das Token zuerst im JWT-Decoder an. - Das Teilergebnis ignorieren.
"aGk=!!"liefert"hi"und einen Fehler. Verwenden Sie die Bytes nicht, wennerr != nilist. Close()am Encoder vergessen. Der letzte Block geht verloren.- Leerzeichen durch Kopieren und Einfügen. Der Decoder überspringt nur
\rund\n. Entfernen Sie anderen Leerraum vorher mitstrings.Join(strings.Fields(s), "").
Versionshinweise
RawStdEncodingundRawURLEncoding: Go 1.5.Strict(): Go 1.8.AppendEncode,AppendDecode: Go 1.22.- Das Paket hat keinen Encoder mit MIME-Zeilenumbruch; die Pakete
mimeundnet/mailkümmern sich um E-Mail, oder Sie fügen selbst alle 76 Zeichen\r\nein.
Häufige Fragen
Was ist der Unterschied zwischen URLEncoding und RawURLEncoding?
Beide verwenden das URL-sichere Alphabet mit - und _. URLEncoding schreibt =-Padding und verlangt es beim Dekodieren; RawURLEncoding lässt das Padding weg und lehnt es ab. JWTs verwenden RawURLEncoding.
Was bedeutet „illegal base64 data at input byte 0“?
Das ist ein base64.CorruptInputError, und die Zahl ist der Offset, an dem das Dekodieren gescheitert ist. Bei Byte 0 und einem kurzen String ist meist die Länge falsch, oft fehlt das Padding; verwenden Sie für Eingaben ohne Padding die Raw-Kodierung.
Ignoriert der Base64-Decoder von Go Zeilenumbrüche?
Ja. \r und \n werden von allen vier Kodierungen übersprungen, MIME-umbrochene Eingaben lassen sich also dekodieren. Leerzeichen und andere Zeichen sind Fehler.