Decoding Base64 in Golang (encoding/base64)

Published: 2023-11-22 | Category: Development

Go (Golang) is highly favored for backend microservices and API gateways. Handling Base64 efficiently is a common requirement in these environments. Go provides the robust encoding/base64 package in its standard library to handle this seamlessly.

The encoding/base64 Package

Go uses different variables in the base64 package to represent different encoding alphabets. The two most common are:

Both encodings expect padding (=) by default. If your data is unpadded, you use their "Raw" counterparts: RawStdEncoding or RawURLEncoding.

Basic Base64 Decoding in Go

Here is how to decode a standard Base64 string into a byte slice, and then convert that byte slice into a string.

Go
package main

import (
	"encoding/base64"
	"fmt"
	"log"
)

func main() {
	encodedStr := "SGVsbG8sIEdvbGFuZyE="

	// DecodeString returns a byte slice ([]byte) and an error
	decodedBytes, err := base64.StdEncoding.DecodeString(encodedStr)
	if err != nil {
		log.Fatalf("Failed to decode: %v", err)
	}

	// Cast the byte slice to a string to print it
	fmt.Println(string(decodedBytes)) 
	// Output: Hello, Golang!
}

Encoding to Base64 in Go

To encode, you pass a byte slice to EncodeToString.

Go
package main

import (
	"encoding/base64"
	"fmt"
)

func main() {
	originalText := "Secure Data 123"

	// Convert string to byte slice for encoding
	encodedStr := base64.StdEncoding.EncodeToString([]byte(originalText))

	fmt.Println(encodedStr)
	// Output: U2VjdXJlIERhdGEgMTIz
}

Handling URL-Safe and Unpadded Base64 (JWTs)

When working with JSON Web Tokens (JWTs) in Go, the headers and payloads are encoded using Raw URL-Safe Base64. This means they use the URL alphabet and have no padding.

Go
package main

import (
	"encoding/base64"
	"fmt"
)

func main() {
	// A typical JWT payload segment (no padding, URL safe)
	jwtPayload := "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9"

	// Use RawURLEncoding to decode unpadded URL-safe strings
	decoded, err := base64.RawURLEncoding.DecodeString(jwtPayload)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Println(string(decoded))
}

High-Performance Stream Processing

If you are processing large files (like uploading a 100MB Base64-encoded backup file via an API), holding the entire string in memory will spike your application's RAM usage.

Go elegantly solves this using the io.Reader and io.Writer interfaces. You can wrap an input stream with a Base64 decoder stream, allowing you to process data in tiny chunks.

Go
package main

import (
	"encoding/base64"
	"io"
	"os"
	"strings"
)

func main() {
	// Simulate a massive base64 string stream
	base64Stream := strings.NewReader("SGVsbG8sIFN0cmVhbXMh...")

	// Wrap the reader with a Base64 decoder
	decoderReader := base64.NewDecoder(base64.StdEncoding, base64Stream)

	// Copy the decoded data directly to a file on disk
	outputFile, _ := os.Create("output.txt")
	defer outputFile.Close()

	// io.Copy handles the chunking automatically. Minimal RAM usage!
	io.Copy(outputFile, decoderReader)
}

Common Errors

The most common error encountered is illegal base64 data at input byte.... This occurs when:

Conclusion

Go's implementation of Base64 is explicit, safe, and highly performant. By understanding the difference between standard, URL, and raw encodings, and utilizing io.Reader for large payloads, you can build incredibly robust APIs.

If you need to quickly debug a Base64 string from your Go application, use our online Base64 Decoder.