Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

encoding/base64 - Base64 编解码

概述

encoding/base64 包提供了 Base64 编码和解码功能。

Base64 是什么

  • 📦 二进制到文本编码:将二进制数据转换为可打印 ASCII 文本
  • 🔧 RFC 4648 标准:遵循互联网标准规范
  • 📋 64 个字符集:使用 A-Z、a-z、0-9、+、/(共 64 个字符)
  • 🛠️ 区分大小写:编码输出区分大小写

主要用途

  • 🌐 Data URI:在 HTML/CSS 中嵌入资源
  • 📧 邮件附件:MIME 邮件编码
  • 🔐 JWT 令牌:JSON Web Token 编码
  • 📊 API 数据传输:RESTful API 中的二进制数据
  • 🖼️ 图片嵌入:在文本格式中嵌入图片
  • 🔑 密钥编码:加密密钥的文本表示

重要说明

  • ⚠️ 空间效率:编码后数据增加约 33%(相比原始数据)
  • ⚠️ 区分大小写:编码和解码都区分大小写
  • ⚠️ 填充字符:使用 = 作为填充
  • ⚠️ 特殊字符:包含 + 和 /,URL 中需要特殊处理
  • 标准库支持:Go 标准库提供完整支持
  • 流式处理:支持 Encoder/Decoder 流式编解码
  • URL 安全变体:提供 URL 安全的编码方案

与 Base32 的比较

特性Base64Base32
字符集大小6432
字符集A-Z, a-z, 0-9, +, /A-Z, 2-7
空间效率+33%+60%
大小写敏感
特殊字符+, /
URL 安全需要变体原生支持
适用场景通用文件名、口头

Base64 编码原理

编码算法

基本步骤

  1. 将输入数据按 3 字节(24 位)分组
  2. 将 24 位数据分成 4 个 6 位组
  3. 每个 6 位组映射到一个 Base64 字符(0-63)
  4. 如果最后不足 3 字节,使用 = 填充

编码效率

3 字节二进制数据 = 24 位
4 个 Base64 字符 = 4 × 6 位 = 24 位
空间效率:4/3 ≈ 1.33(增加 33%)

字符集

标准 Base64 字符集(RFC 4648)

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
0123456789012345678901234567890123456789012345678901234567890123

字符映射

值 0-25   → A-Z
值 26-51  → a-z
值 52-61  → 0-9
值 62     → +
值 63     → /

URL 安全字符集

值 62     → -(减号代替 +)
值 63     → _(下划线代替 /)

填充规则

输入字节数 | 输出字符数 | 填充数
----------|-----------|-------
    1     |     4     |   2 (=)
    2     |     4     |   1 (=)
    3     |     4     |   0
    3n    |    4n     |   0

核心类型

1. Encoding - 编码器

type Encoding struct {
    // 包含过滤或未导出的字段
}

功能:表示一个 Base64 编码器配置。

预定义编码器

var (
    StdEncoding     *Encoding  // 标准 Base64
    URLEncoding     *Encoding  // URL 安全 Base64
    RawStdEncoding  *Encoding  // 无填充标准 Base64
    RawURLEncoding  *Encoding  // 无填充 URL 安全 Base64
)

主要方法

// 编码
func (enc *Encoding) Encode(dst, src []byte)
func (enc *Encoding) EncodeToString(src []byte) string

// 解码
func (enc *Encoding) Decode(dst, src []byte) (int, error)
func (enc *Encoding) DecodeString(s string) ([]byte, error)

// 长度计算
func (enc *Encoding) EncodedLen(n int) int
func (enc *Encoding) DecodedLen(n int) int

// 流式编解码
func (enc *Encoding) NewEncoder(w io.Writer) *Encoder
func (enc *Encoding) NewDecoder(r io.Reader) *Decoder

// 严格模式
func (enc *Encoding) Strict() *Encoding

2. Encoder - 编码流

type Encoder struct {
    // 包含过滤或未导出的字段
}

功能:将数据流式编码为 Base64。

主要方法

// 写入数据
func (e *Encoder) Write(p []byte) (n int, err error)

// 关闭编码器(写入填充)
func (e *Encoder) Close() error

使用示例

var buf strings.Builder
encoder := base64.StdEncoding.NewEncoder(&buf)
encoder.Write([]byte("Hello"))
encoder.Close()
fmt.Println(buf.String())

3. Decoder - 解码流

type Decoder struct {
    // 包含过滤或未导出的字段
}

功能:从 Base64 数据流式解码。

主要方法

// 读取数据
func (d *Decoder) Read(p []byte) (n int, err error)

使用示例

decoder := base64.StdEncoding.NewDecoder(strings.NewReader("SGVsbG8="))
data := make([]byte, 100)
n, err := decoder.Read(data)

预定义编码器

StdEncoding - 标准 Base64

var StdEncoding = NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")

特点

  • ✅ 标准 Base64 字符集
  • ✅ 使用 = 填充
  • ✅ 包含 + 和 / 特殊字符
  • ⚠️ 不适合直接用于 URL

使用场景

  • 邮件附件(MIME)
  • Data URI
  • JWT 令牌
  • 通用二进制编码

示例

data := []byte("Hello, World!")
encoded := base64.StdEncoding.EncodeToString(data)
// 输出:SGVsbG8sIFdvcmxkIQ==

URLEncoding - URL 安全 Base64

var URLEncoding = NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_")

特点

  • ✅ 使用 - 代替 +
  • ✅ 使用 _ 代替 /
  • ✅ 适合 URL 和文件名
  • ✅ 使用 = 填充

使用场景

  • URL 参数
  • 文件名
  • JWT 令牌(JWS/JWE)
  • 数据库键值

示例

data := []byte("Hello+World/Test")
encoded := base64.URLEncoding.EncodeToString(data)
// 输出:SGVsbG8rV29ybGQvVGVzdA==

RawStdEncoding - 无填充标准 Base64

var RawStdEncoding = StdEncoding.WithPadding(base64.NoPadding)

特点

  • ✅ 标准 Base64 字符集
  • ✅ 不使用填充
  • ✅ 更紧凑
  • ⚠️ 需要知道原始数据长度

使用场景

  • 已知长度的二进制数据
  • 紧凑存储
  • 协议字段

示例

data := []byte("Hi")
encoded := base64.RawStdEncoding.EncodeToString(data)
// 输出:SGk(无填充)

// 标准编码对比
stdEncoded := base64.StdEncoding.EncodeToString(data)
// 输出:SGk=(有填充)

RawURLEncoding - 无填充 URL 安全 Base64

var RawURLEncoding = URLEncoding.WithPadding(base64.NoPadding)

特点

  • ✅ URL 安全字符集
  • ✅ 不使用填充
  • ✅ 最紧凑的 Base64 编码

使用场景

  • URL 参数(紧凑)
  • JWT 令牌头部和签名
  • 数据库键值

示例

data := []byte("Hi")
encoded := base64.RawURLEncoding.EncodeToString(data)
// 输出:SGk(无填充)

核心函数

基本编解码

// 编码到字符串
func StdEncoding.EncodeToString(src []byte) string

// 从字符串解码
func StdEncoding.DecodeString(s string) ([]byte, error)

// 编码到缓冲区
func StdEncoding.Encode(dst, src []byte)

// 从缓冲区解码
func StdEncoding.Decode(dst, src []byte) (int, error)

// 长度计算
func StdEncoding.EncodedLen(n int) int
func StdEncoding.DecodedLen(n int) int

自定义编码器

// 创建自定义编码器
func NewEncoding(encoder string) *Encoding

// 修改填充字符
func (enc *Encoding) WithPadding(padding rune) *Encoding

完整示例

示例 1:基本编解码

package main

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

func main() {
    // 原始数据
    data := []byte("Hello, Base64!")
    fmt.Printf("原始数据:%s\n", string(data))
    fmt.Printf("原始长度:%d 字节\n\n", len(data))
    
    // 标准 Base64 编码
    encoded := base64.StdEncoding.EncodeToString(data)
    fmt.Printf("标准 Base64: %s\n", encoded)
    fmt.Printf("编码长度:%d 字符\n\n", len(encoded))
    
    // URL 安全 Base64 编码
    urlEncoded := base64.URLEncoding.EncodeToString(data)
    fmt.Printf("URL 安全 Base64: %s\n", urlEncoded)
    fmt.Printf("编码长度:%d 字符\n\n", len(urlEncoded))
    
    // 解码
    decoded, err := base64.StdEncoding.DecodeString(encoded)
    if err != nil {
        log.Fatalf("解码失败:%v", err)
    }
    fmt.Printf("解码结果:%s\n", string(decoded))
    fmt.Printf("解码长度:%d 字节\n", len(decoded))
    
    // 验证
    if string(decoded) == string(data) {
        fmt.Println("\n✓ 编解码成功!")
    }
    
    // 长度对比
    fmt.Printf("\n长度对比:\n")
    fmt.Printf("原始:%d 字节\n", len(data))
    fmt.Printf("Base64: %d 字符 (+%.0f%%)\n", 
        len(encoded), 
        float64(len(encoded)-len(data))/float64(len(data))*100)
}

输出

原始数据:Hello, Base64!
原始长度:14 字节

标准 Base64: SGVsbG8sIEJhc2U2NCE=
编码长度:20 字符

URL 安全 Base64: SGVsbG8sIEJhc2U2NCE=
编码长度:20 字符

解码结果:Hello, Base64!
解码长度:14 字节

✓ 编解码成功!

长度对比:
原始:14 字节
Base64: 20 字符 (+43%)

示例 2:不同编码器对比

package main

import (
    "encoding/base64"
    "fmt"
)

func main() {
    // 测试数据
    testData := []byte{
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 
        0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
    }
    
    fmt.Println("=== 不同编码器对比 ===\n")
    
    // 1. 标准 Base64
    stdEncoded := base64.StdEncoding.EncodeToString(testData)
    fmt.Printf("标准 Base64:\n")
    fmt.Printf("  编码:%s\n", stdEncoded)
    fmt.Printf("  长度:%d 字符\n", len(stdEncoded))
    fmt.Printf("  填充:%s\n\n", getPadding(stdEncoded))
    
    // 2. URL 安全 Base64
    urlEncoded := base64.URLEncoding.EncodeToString(testData)
    fmt.Printf("URL 安全 Base64:\n")
    fmt.Printf("  编码:%s\n", urlEncoded)
    fmt.Printf("  长度:%d 字符\n", len(urlEncoded))
    fmt.Printf("  填充:%s\n\n", getPadding(urlEncoded))
    
    // 3. 无填充标准 Base64
    rawStdEncoded := base64.RawStdEncoding.EncodeToString(testData)
    fmt.Printf("无填充标准 Base64:\n")
    fmt.Printf("  编码:%s\n", rawStdEncoded)
    fmt.Printf("  长度:%d 字符\n", len(rawStdEncoded))
    fmt.Printf("  填充:无\n\n")
    
    // 4. 无填充 URL 安全 Base64
    rawURLEncoded := base64.RawURLEncoding.EncodeToString(testData)
    fmt.Printf("无填充 URL 安全 Base64:\n")
    fmt.Printf("  编码:%s\n", rawURLEncoded)
    fmt.Printf("  长度:%d 字符\n", len(rawURLEncoded))
    fmt.Printf("  填充:无\n\n")
    
    // 验证解码
    fmt.Println("=== 解码验证 ===")
    decoded, _ := base64.StdEncoding.DecodeString(stdEncoded)
    fmt.Printf("标准解码:%x\n", decoded)
    
    decoded, _ = base64.URLEncoding.DecodeString(urlEncoded)
    fmt.Printf("URL 解码:%x\n", decoded)
    
    decoded, _ = base64.RawStdEncoding.DecodeString(rawStdEncoded)
    fmt.Printf("无填充标准解码:%x\n", decoded)
    
    decoded, _ = base64.RawURLEncoding.DecodeString(rawURLEncoded)
    fmt.Printf("无填充 URL 解码:%x\n", decoded)
}

// getPadding 获取填充信息
func getPadding(s string) string {
    for i := len(s) - 1; i >= 0; i-- {
        if s[i] != '=' {
            return "无"
        }
    }
    return "无"
}

示例 3:Data URI 应用

package main

import (
    "encoding/base64"
    "fmt"
    "net/http"
    "os"
)

// GenerateDataURI 生成 Data URI
func GenerateDataURI(mimeType string, data []byte) string {
    encoded := base64.StdEncoding.EncodeToString(data)
    return fmt.Sprintf("data:%s;base64,%s", mimeType, encoded)
}

// ParseDataURI 解析 Data URI
func ParseDataURI(dataURI string) (mimeType string, data []byte, err error) {
    // 验证格式
    if len(dataURI) < 13 || dataURI[:5] != "data:" {
        return "", nil, fmt.Errorf("无效的 Data URI 格式")
    }
    
    // 分割 MIME 和编码数据
    commaIndex := -1
    for i := 5; i < len(dataURI); i++ {
        if dataURI[i] == ',' {
            commaIndex = i
            break
        }
    }
    
    if commaIndex == -1 {
        return "", nil, fmt.Errorf("无效的 Data URI 格式")
    }
    
    // 提取 MIME 类型
    mimeType = dataURI[5:commaIndex]
    if len(mimeType) > 7 && mimeType[len(mimeType)-7:] == ";base64" {
        mimeType = mimeType[:len(mimeType)-7]
    }
    
    // 解码 Base64 数据
    data, err = base64.StdEncoding.DecodeString(dataURI[commaIndex+1:])
    if err != nil {
        return "", nil, err
    }
    
    return mimeType, data, nil
}

// EmbedImage 在 HTML 中嵌入图片
func EmbedImage(imagePath string) (string, error) {
    // 读取图片
    data, err := os.ReadFile(imagePath)
    if err != nil {
        return "", err
    }
    
    // 确定 MIME 类型
    mimeType := "image/png"
    if len(imagePath) > 4 && imagePath[len(imagePath)-4:] == ".jpg" {
        mimeType = "image/jpeg"
    } else if len(imagePath) > 4 && imagePath[len(imagePath)-4:] == ".gif" {
        mimeType = "image/gif"
    } else if len(imagePath) > 4 && imagePath[len(imagePath)-4:] == ".svg" {
        mimeType = "image/svg+xml"
    }
    
    // 生成 Data URI
    return GenerateDataURI(mimeType, data), nil
}

func main() {
    fmt.Println("=== Data URI 生成器 ===\n")
    
    // 示例 1:小图片
    pngData := []byte{ /* PNG 图片数据 */ }
    dataURI := GenerateDataURI("image/png", pngData)
    fmt.Printf("图片 Data URI(前 100 字符):\n%s...\n\n", dataURI[:100])
    
    // 示例 2:文本数据
    textData := []byte("Hello, Data URI!")
    textURI := GenerateDataURI("text/plain", textData)
    fmt.Printf("文本 Data URI: %s\n", textURI)
    
    // 解析验证
    mimeType, decoded, err := ParseDataURI(textURI)
    if err != nil {
        fmt.Printf("解析失败:%v\n", err)
    } else {
        fmt.Printf("\n解析结果:\n")
        fmt.Printf("  MIME 类型:%s\n", mimeType)
        fmt.Printf("  解码数据:%s\n", string(decoded))
    }
    
    // 示例 3:HTML 中使用
    fmt.Println("\n=== HTML 示例 ===")
    html := `<!DOCTYPE html>
<html>
<head><title>Data URI 示例</title></head>
<body>
    <h1>嵌入图片</h1>
    <img src="%s" alt="Embedded Image">
</body>
</html>`
    
    fmt.Printf(html, dataURI)
    
    // 示例 4:HTTP 服务器
    fmt.Println("\n\n=== HTTP 服务器示例 ===")
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "text/html; charset=utf-8")
        fmt.Fprintf(w, html, dataURI)
    })
    
    fmt.Println("服务器代码已定义,使用方法:")
    fmt.Println("  http.ListenAndServe(\":8080\", nil)")
}

示例 4:JWT 令牌处理

package main

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

// JWTHeader JWT 头部
type JWTHeader struct {
    Alg string `json:"alg"`
    Typ string `json:"typ"`
}

// JWTPayload JWT 载荷
type JWTPayload struct {
    Sub   string `json:"sub"`
    Name  string `json:"name"`
    Admin bool   `json:"admin"`
    Iat   int    `json:"iat"`
}

// EncodeBase64URL 编码 JWT 片段
func EncodeBase64URL(data []byte) string {
    return base64.RawURLEncoding.EncodeToString(data)
}

// DecodeBase64URL 解码 JWT 片段
func DecodeBase64URL(s string) ([]byte, error) {
    // 添加填充(如果需要)
    switch len(s) % 4 {
    case 2:
        s += "=="
    case 3:
        s += "="
    }
    return base64.URLEncoding.DecodeString(s)
}

// CreateJWT 创建 JWT 令牌
func CreateJWT(header JWTHeader, payload JWTPayload, signature []byte) (string, error) {
    // 编码头部
    headerJSON, err := json.Marshal(header)
    if err != nil {
        return "", err
    }
    headerEncoded := EncodeBase64URL(headerJSON)
    
    // 编码载荷
    payloadJSON, err := json.Marshal(payload)
    if err != nil {
        return "", err
    }
    payloadEncoded := EncodeBase64URL(payloadJSON)
    
    // 编码签名
    signatureEncoded := EncodeBase64URL(signature)
    
    // 组合 JWT
    return fmt.Sprintf("%s.%s.%s", headerEncoded, payloadEncoded, signatureEncoded), nil
}

// ParseJWT 解析 JWT 令牌
func ParseJWT(token string) (*JWTHeader, *JWTPayload, error) {
    parts := strings.Split(token, ".")
    if len(parts) != 3 {
        return nil, nil, fmt.Errorf("无效的 JWT 格式")
    }
    
    // 解码头部
    headerData, err := DecodeBase64URL(parts[0])
    if err != nil {
        return nil, nil, err
    }
    
    var header JWTHeader
    if err := json.Unmarshal(headerData, &header); err != nil {
        return nil, nil, err
    }
    
    // 解码载荷
    payloadData, err := DecodeBase64URL(parts[1])
    if err != nil {
        return nil, nil, err
    }
    
    var payload JWTPayload
    if err := json.Unmarshal(payloadData, &payload); err != nil {
        return nil, nil, err
    }
    
    return &header, &payload, nil
}

func main() {
    fmt.Println("=== JWT 令牌处理 ===\n")
    
    // 创建 JWT
    header := JWTHeader{
        Alg: "HS256",
        Typ: "JWT",
    }
    
    payload := JWTPayload{
        Sub:   "1234567890",
        Name:  "John Doe",
        Admin: true,
        Iat:   1516239022,
    }
    
    signature := []byte("signature-placeholder")
    
    token, err := CreateJWT(header, payload, signature)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("JWT 令牌:\n%s\n\n", token)
    
    // 解析 JWT
    parsedHeader, parsedPayload, err := ParseJWT(token)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("解析结果:\n")
    fmt.Printf("  头部:Alg=%s, Typ=%s\n", parsedHeader.Alg, parsedHeader.Typ)
    fmt.Printf("  载荷:Sub=%s, Name=%s, Admin=%v, Iat=%d\n",
        parsedPayload.Sub, parsedPayload.Name, parsedPayload.Admin, parsedPayload.Iat)
    
    // 显示 Base64 编码细节
    fmt.Println("\n=== Base64 编码细节 ===")
    parts := strings.Split(token, ".")
    for i, part := range parts {
        fmt.Printf("部分 %d: %s\n", i, part)
        fmt.Printf("  长度:%d 字符\n", len(part))
        fmt.Printf("  无填充:%v\n", !strings.Contains(part, "="))
    }
}

示例 5:流式编解码

package main

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

// EncodeFile 编码文件
func EncodeFile(inputPath, outputPath string) error {
    inputFile, err := os.Open(inputPath)
    if err != nil {
        return err
    }
    defer inputFile.Close()
    
    outputFile, err := os.Create(outputPath)
    if err != nil {
        return err
    }
    defer outputFile.Close()
    
    // 创建 Base64 编码器
    encoder := base64.NewEncoder(base64.StdEncoding, outputFile)
    defer encoder.Close()
    
    // 分块复制
    buffer := make([]byte, 4096)
    for {
        n, err := inputFile.Read(buffer)
        if n > 0 {
            encoder.Write(buffer[:n])
        }
        if err == io.EOF {
            break
        }
        if err != nil {
            return err
        }
    }
    
    return nil
}

// DecodeFile 解码文件
func DecodeFile(inputPath, outputPath string) error {
    inputFile, err := os.Open(inputPath)
    if err != nil {
        return err
    }
    defer inputFile.Close()
    
    outputFile, err := os.Create(outputPath)
    if err != nil {
        return err
    }
    defer outputFile.Close()
    
    // 创建 Base64 解码器
    decoder := base64.NewDecoder(base64.StdEncoding, inputFile)
    
    // 分块复制
    buffer := make([]byte, 4096)
    for {
        n, err := decoder.Read(buffer)
        if n > 0 {
            outputFile.Write(buffer[:n])
        }
        if err == io.EOF {
            break
        }
        if err != nil {
            return err
        }
    }
    
    return nil
}

// EncodeString 流式编码字符串
func EncodeString(data string) string {
    var buf strings.Builder
    encoder := base64.NewEncoder(base64.StdEncoding, &buf)
    encoder.Write([]byte(data))
    encoder.Close()
    return buf.String()
}

// DecodeString 流式解码字符串
func DecodeString(encoded string) (string, error) {
    decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(encoded))
    data, err := io.ReadAll(decoder)
    if err != nil {
        return "", err
    }
    return string(data), nil
}

func main() {
    fmt.Println("=== 流式编解码示例 ===\n")
    
    // 示例 1:字符串流式编码
    original := "This is a test of streaming Base64 encoding."
    encoded := EncodeString(original)
    fmt.Printf("原始字符串:%s\n", original)
    fmt.Printf("编码后:%s\n\n", encoded)
    
    // 流式解码
    decoded, err := DecodeString(encoded)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("解码后:%s\n", decoded)
    fmt.Printf("验证:%v\n\n", decoded == original)
    
    // 示例 2:大文件编码(模拟)
    fmt.Println("=== 大文件编码示例 ===")
    
    // 创建测试文件
    testData := strings.Repeat("Large file content for testing. ", 1000)
    os.WriteFile("test_input.txt", []byte(testData), 0644)
    
    // 编码
    err = EncodeFile("test_input.txt", "test_output.b64")
    if err != nil {
        log.Fatal(err)
    }
    
    // 解码
    err = DecodeFile("test_output.b64", "test_restored.txt")
    if err != nil {
        log.Fatal(err)
    }
    
    // 验证
    restored, _ := os.ReadFile("test_restored.txt")
    fmt.Printf("原始大小:%d 字节\n", len(testData))
    fmt.Printf("编码大小:%d 字符\n", len(encoded))
    fmt.Printf("恢复大小:%d 字节\n", len(restored))
    fmt.Printf("验证:%v\n", string(restored) == testData)
    
    // 清理测试文件
    os.Remove("test_input.txt")
    os.Remove("test_output.b64")
    os.Remove("test_restored.txt")
}

示例 6:URL 安全编码

package main

import (
    "encoding/base64"
    "fmt"
    "net/url"
)

// GenerateSafeURL 生成安全的 URL
func GenerateSafeURL(baseURL string, params map[string][]byte) string {
    u, _ := url.Parse(baseURL)
    q := u.Query()
    
    for key, value := range params {
        // 使用 URL 安全 Base64 编码
        encoded := base64.URLEncoding.EncodeToString(value)
        q.Set(key, encoded)
    }
    
    u.RawQuery = q.Encode()
    return u.String()
}

// ParseSafeURL 解析安全的 URL
func ParseSafeURL(encodedURL string) (map[string][]byte, error) {
    u, err := url.Parse(encodedURL)
    if err != nil {
        return nil, err
    }
    
    result := make(map[string][]byte)
    for key, value := range u.Query() {
        if len(value) > 0 {
            decoded, err := base64.URLEncoding.DecodeString(value[0])
            if err != nil {
                return nil, err
            }
            result[key] = decoded
        }
    }
    
    return result, nil
}

func main() {
    fmt.Println("=== URL 安全 Base64 编码 ===\n")
    
    // 示例 1:基本 URL 安全编码
    data := []byte("Hello+World/Test?Query=Value")
    
    stdEncoded := base64.StdEncoding.EncodeToString(data)
    urlEncoded := base64.URLEncoding.EncodeToString(data)
    
    fmt.Printf("原始数据:%s\n", string(data))
    fmt.Printf("标准 Base64: %s\n", stdEncoded)
    fmt.Printf("URL 安全:%s\n\n", urlEncoded)
    
    // 检查特殊字符
    fmt.Printf("标准编码包含 +: %v\n", contains(stdEncoded, '+'))
    fmt.Printf("标准编码包含 /: %v\n", contains(stdEncoded, '/'))
    fmt.Printf("URL 编码包含 +: %v\n", contains(urlEncoded, '+'))
    fmt.Printf("URL 编码包含 /: %v\n", contains(urlEncoded, '/'))
    
    // 示例 2:URL 参数
    fmt.Println("\n=== URL 参数示例 ===")
    baseURL := "https://example.com/api"
    params := map[string][]byte{
        "token": []byte("secret-token-123"),
        "data":  []byte("sensitive-data-456"),
    }
    
    safeURL := GenerateSafeURL(baseURL, params)
    fmt.Printf("生成的 URL:\n%s\n\n", safeURL)
    
    // 解析 URL
    parsedParams, err := ParseSafeURL(safeURL)
    if err != nil {
        fmt.Printf("解析失败:%v\n", err)
    } else {
        fmt.Println("解析结果:")
        for key, value := range parsedParams {
            fmt.Printf("  %s: %s\n", key, string(value))
        }
    }
    
    // 示例 3:作为文件名
    fmt.Println("\n=== 文件名示例 ===")
    filename := base64.URLEncoding.EncodeToString([]byte("user/document.pdf"))
    fmt.Printf("安全文件名:%s\n", filename)
    
    // 验证文件名安全性
    fmt.Printf("包含 /: %v\n", contains(filename, '/'))
    fmt.Printf("包含 \\: %v\n", contains(filename, '\\'))
    fmt.Printf("包含 :: %v\n", contains(filename, ':'))
}

// contains 检查字符串是否包含指定字符
func contains(s string, c byte) bool {
    for i := 0; i < len(s); i++ {
        if s[i] == c {
            return true
        }
    }
    return false
}

示例 7:错误处理

package main

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

func main() {
    // 1. 有效的 Base64 字符串
    validCases := []string{
        "SGVsbG8=",           // 正确填充
        "SGVsbG8",            // 无填充(可接受)
        "SGVs",               // 2 字符填充
        "SG==",               // 1 字节数据
        "SGVsbG8sIFdvcmxkIQ==", // 长字符串
    }
    
    fmt.Println("=== 有效输入 ===")
    for _, s := range validCases {
        decoded, err := base64.StdEncoding.DecodeString(s)
        if err != nil {
            fmt.Printf("✗ %s -> 错误:%v\n", s, err)
        } else {
            fmt.Printf("✓ %s -> %s (%d 字节)\n", s, string(decoded), len(decoded))
        }
    }
    
    // 2. 无效的 Base64 字符串
    fmt.Println("\n=== 无效输入 ===")
    invalidCases := []struct {
        input       string
        description string
    }{
        {"SGVs!", "包含无效字符 !"},
        {"SGVs ", "包含空格"},
        {"SG", "长度错误(缺少填充)"},
        {"SGVsbG8===", "填充过多"},
        {"=SGVsbG8", "填充位置错误"},
    }
    
    for _, tc := range invalidCases {
        decoded, err := base64.StdEncoding.DecodeString(tc.input)
        if err != nil {
            fmt.Printf("✗ %s (%s) -> 错误:%v\n", tc.input, tc.description, err)
        } else {
            fmt.Printf("? %s (%s) -> %x (可能已自动修正)\n", tc.input, tc.description, decoded)
        }
    }
    
    // 3. 缓冲区大小错误
    fmt.Println("\n=== 缓冲区大小测试 ===")
    data := []byte("Test data")
    encoded := make([]byte, base64.StdEncoding.EncodedLen(len(data)))
    base64.StdEncoding.Encode(encoded, data)
    
    // 正确的缓冲区大小
    decoded := make([]byte, base64.StdEncoding.DecodedLen(len(encoded)))
    n, err := base64.StdEncoding.Decode(decoded, encoded)
    if err != nil {
        log.Printf("解码错误:%v", err)
    } else {
        fmt.Printf("✓ 正确缓冲区:%s (%d 字节)\n", string(decoded[:n]), n)
    }
    
    // 过小的缓冲区
    smallBuf := make([]byte, 2)
    n, err = base64.StdEncoding.Decode(smallBuf, encoded)
    if err != nil {
        fmt.Printf("✗ 缓冲区过小:%v\n", err)
    } else {
        fmt.Printf("? 部分解码:%d 字节\n", n)
    }
    
    // 4. 严格模式
    fmt.Println("\n=== 严格模式 ===")
    strictEncoding := base64.StdEncoding.Strict()
    
    strictCases := []string{
        "SGVsbG8=",      // 正确
        "SGVsbG8",       // 缺少填充
        "SGVs!G8=",      // 无效字符
    }
    
    for _, s := range strictCases {
        decoded, err := strictEncoding.DecodeString(s)
        if err != nil {
            fmt.Printf("✗ %s -> 错误:%v\n", s, err)
        } else {
            fmt.Printf("✓ %s -> %s\n", s, string(decoded))
        }
    }
}

最佳实践

✅ 推荐做法

  1. 使用预定义编码器

    // ✅ 推荐
    encoded := base64.StdEncoding.EncodeToString(data)
    encoded := base64.URLEncoding.EncodeToString(data)
    
    // ❌ 不推荐:创建自定义编码器(除非必要)
    
  2. URL 中使用安全编码

    // ✅ 推荐:URL 参数
    encoded := base64.URLEncoding.EncodeToString(data)
    
    // ❌ 不推荐:标准编码(包含 + 和 /)
    encoded := base64.StdEncoding.EncodeToString(data)
    
  3. 处理用户输入

    // 添加填充(如果需要)
    for len(s)%4 != 0 {
        s += "="
    }
    decoded, err := base64.StdEncoding.DecodeString(s)
    
  4. 流式处理大文件

    encoder := base64.NewEncoder(base64.StdEncoding, outputFile)
    defer encoder.Close()
    io.Copy(encoder, inputFile)
    

❌ 不安全做法

  1. 不要忽略错误

    // ❌ 错误
    decoded, _ := base64.StdEncoding.DecodeString(input)
    
    // ✅ 正确
    decoded, err := base64.StdEncoding.DecodeString(input)
    if err != nil {
        return err
    }
    
  2. 不要混用编码器

    // ❌ 错误
    encoded := base64.StdEncoding.EncodeToString(data)
    decoded, _ := base64.URLEncoding.DecodeString(encoded)  // 可能失败
    
    // ✅ 正确
    encoded := base64.URLEncoding.EncodeToString(data)
    decoded, _ := base64.URLEncoding.DecodeString(encoded)
    

性能优化

预分配缓冲区

// ✅ 推荐:预分配缓冲区
encoded := make([]byte, base64.StdEncoding.EncodedLen(len(data)))
base64.StdEncoding.Encode(encoded, data)

// ❌ 不推荐:动态增长
var encoded []byte
for _, b := range data {
    encoded = append(encoded, encodeByte(b))
}

批量处理

// ✅ 推荐:批量编码
encoded := base64.StdEncoding.EncodeToString(largeData)

// ❌ 不推荐:逐行编码
var result string
for _, line := range lines {
    result += base64.StdEncoding.EncodeToString(line)
}

总结

核心类型

类型用途说明
Encoding编码器配置定义编码规则
Encoder编码流流式编码
Decoder解码流流式解码

预定义编码器

编码器字符集填充用途
StdEncodingA-Z, a-z, 0-9, +, /=标准 Base64
URLEncodingA-Z, a-z, 0-9, -, _=URL 安全
RawStdEncodingA-Z, a-z, 0-9, +, /无填充标准
RawURLEncodingA-Z, a-z, 0-9, -, _无填充 URL

核心方法

方法用途返回值
EncodeToString编码为字符串string
DecodeString从字符串解码[]byte, error
Encode编码到缓冲区-
Decode从缓冲区解码int, error
EncodedLen计算编码长度int
DecodedLen计算解码长度int

使用场景

场景推荐编码器说明
邮件附件StdEncodingMIME 标准
Data URIStdEncodingHTML/CSS嵌入
JWT 令牌RawURLEncoding紧凑 URL 安全
URL 参数URLEncoding安全传输
大文件NewEncoder/NewDecoder流式处理
API 数据URLEncodingWeb API

字符集对比

特性Base64Base32Base16
字符数643216
大小写敏感不敏感不敏感
特殊字符+, /
空间效率+33%+60%+100%
人类可读较好最好

参考资料


最后更新:2026-04-03
Go 版本:Go 1.23+