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 的比较:
| 特性 | Base64 | Base32 |
|---|---|---|
| 字符集大小 | 64 | 32 |
| 字符集 | A-Z, a-z, 0-9, +, / | A-Z, 2-7 |
| 空间效率 | +33% | +60% |
| 大小写敏感 | 是 | 否 |
| 特殊字符 | +, / | 无 |
| URL 安全 | 需要变体 | 原生支持 |
| 适用场景 | 通用 | 文件名、口头 |
Base64 编码原理
编码算法
基本步骤:
- 将输入数据按 3 字节(24 位)分组
- 将 24 位数据分成 4 个 6 位组
- 每个 6 位组映射到一个 Base64 字符(0-63)
- 如果最后不足 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))
}
}
}
最佳实践
✅ 推荐做法
-
使用预定义编码器
// ✅ 推荐 encoded := base64.StdEncoding.EncodeToString(data) encoded := base64.URLEncoding.EncodeToString(data) // ❌ 不推荐:创建自定义编码器(除非必要) -
URL 中使用安全编码
// ✅ 推荐:URL 参数 encoded := base64.URLEncoding.EncodeToString(data) // ❌ 不推荐:标准编码(包含 + 和 /) encoded := base64.StdEncoding.EncodeToString(data) -
处理用户输入
// 添加填充(如果需要) for len(s)%4 != 0 { s += "=" } decoded, err := base64.StdEncoding.DecodeString(s) -
流式处理大文件
encoder := base64.NewEncoder(base64.StdEncoding, outputFile) defer encoder.Close() io.Copy(encoder, inputFile)
❌ 不安全做法
-
不要忽略错误
// ❌ 错误 decoded, _ := base64.StdEncoding.DecodeString(input) // ✅ 正确 decoded, err := base64.StdEncoding.DecodeString(input) if err != nil { return err } -
不要混用编码器
// ❌ 错误 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 | 解码流 | 流式解码 |
预定义编码器
| 编码器 | 字符集 | 填充 | 用途 |
|---|---|---|---|
| StdEncoding | A-Z, a-z, 0-9, +, / | = | 标准 Base64 |
| URLEncoding | A-Z, a-z, 0-9, -, _ | = | URL 安全 |
| RawStdEncoding | A-Z, a-z, 0-9, +, / | 无 | 无填充标准 |
| RawURLEncoding | A-Z, a-z, 0-9, -, _ | 无 | 无填充 URL |
核心方法
| 方法 | 用途 | 返回值 |
|---|---|---|
| EncodeToString | 编码为字符串 | string |
| DecodeString | 从字符串解码 | []byte, error |
| Encode | 编码到缓冲区 | - |
| Decode | 从缓冲区解码 | int, error |
| EncodedLen | 计算编码长度 | int |
| DecodedLen | 计算解码长度 | int |
使用场景
| 场景 | 推荐编码器 | 说明 |
|---|---|---|
| 邮件附件 | StdEncoding | MIME 标准 |
| Data URI | StdEncoding | HTML/CSS嵌入 |
| JWT 令牌 | RawURLEncoding | 紧凑 URL 安全 |
| URL 参数 | URLEncoding | 安全传输 |
| 大文件 | NewEncoder/NewDecoder | 流式处理 |
| API 数据 | URLEncoding | Web API |
字符集对比
| 特性 | Base64 | Base32 | Base16 |
|---|---|---|---|
| 字符数 | 64 | 32 | 16 |
| 大小写 | 敏感 | 不敏感 | 不敏感 |
| 特殊字符 | +, / | 无 | 无 |
| 空间效率 | +33% | +60% | +100% |
| 人类可读 | 较好 | 好 | 最好 |
参考资料
最后更新:2026-04-03
Go 版本:Go 1.23+