Base64编码(Golang)

Base64是一种用64个可打印字符来表示二进制数据的方法。叫64是因为用了64个字符。

一、需求

计算机底层是二进制(0和1),但很多传输通道只支持文本。比如:

  • 电子邮件:早期SMTP协议只支持ASCII字符,传不了二进制附件
  • URL:有些特殊字符在URL里有特殊含义(? & =),二进制数据传不了
  • HTML/CSS:在网页里直接嵌入图片,不需要额外请求
  • JSON/XML:这些文本格式不能直接塞二进制数据

Base64就是把二进制数据”翻译”成纯文本,安全地通过这些通道。


二、原理

每3个字节(24位)分成4组,每组6位,查表得到4个字符。

原始数据:Man
二进制:01001101 01100001 01101110
6位分组:010011 011000 010110 1110(补00)
查表:T W F u
结果:TWFu

如果原始数据不是3的倍数:

剩1个字节 → 输出2个字符 + 2个 “=” 填充

剩2个字节 → 输出3个字符 + 1个 “=” 填充

任何二进制数据都可以,不只是PNG
图片:PNG、JPG、GIF、WEBP、SVG、BMP、ICO
音频:MP3、WAV、AAC、OGG
视频:MP4、AVI、WEBM
文档:PDF、ZIP、EXE、DLL
证书:PEM格式的SSL证书
任意文件:任何你能想到的文件


三、其他编码

Base16(Hex):用16个字符(0-9 A-F),体积膨胀100%
Base32:用32个字符,体积膨胀约60%
Base64:用64个字符,体积膨胀约33%
一张100KB的PNG图片:
base64编码后约133KB
解码后恢复为原始的100KB PNG

Base85:用85个字符,体积膨胀约25%,但字符集更复杂

Base64是体积和可读性的最佳平衡点

四、使用示例

package main

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

func main() {
        //1. 基础字符串编解码
        original := "Hello, 世界! "
        fmt.Printf("原始字符串: %s\n", original)
        encoded := base64.StdEncoding.EncodeToString([]byte(original))
        fmt.Printf("Base64编码: %s\n", encoded)
        decoded, err := base64.StdEncoding.DecodeString(encoded)
        if err != nil {
                fmt.Printf("解码错误: %v\n", err)
        } else {
                fmt.Printf("Base64解码: %s\n", string(decoded))
        }

        // 2. URL安全的Base64
        urlData := "user+name/test=data&key=val"
        fmt.Printf("原始数据: %s\n", urlData)
        stdEncoded := base64.StdEncoding.EncodeToString([]byte(urlData))
        urlEncoded := base64.URLEncoding.EncodeToString([]byte(urlData))
        fmt.Printf("标准Base64: %s\n", stdEncoded)
        fmt.Printf("URL Base64: %s\n", urlEncoded)

        // 3. 无填充的Base64
        data := "abc"
        fmt.Printf("原始数据: %s\n", data)
        fmt.Printf("带填充: %s\n", base64.StdEncoding.EncodeToString([]byte(data)))
        fmt.Printf("无填充: %s\n", base64.RawStdEncoding.EncodeToString([]byte(data)))

        // 4. 自定义Base64字母表
        custom := base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~!")
        customEncoded := custom.EncodeToString([]byte("Hello World"))
        fmt.Printf("自定义编码: %s\n", customEncoded)
        customDecoded, _ := custom.DecodeString(customEncoded)
        fmt.Printf("自定义解码: %s\n", string(customDecoded))

        // 5. 模拟图片Base64编码
        // 创建一个假的PNG文件头(实际项目中替换为真实图片文件)
        fakePNG := []byte{
                0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, // PNG文件头
                0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, // IHDR
        }
        imgEncoded := base64.StdEncoding.EncodeToString(fakePNG)
        fmt.Printf("图片Base64: %s\n", imgEncoded)

        // 模拟HTML内嵌(取前min(30, len)个字符)
        prefixLen := 30
        if len(imgEncoded) < prefixLen {
                prefixLen = len(imgEncoded)
        }
        fmt.Printf("HTML内嵌: <img src=\"data:image/png;base64,%s...\" />\n", imgEncoded[:prefixLen])

        // 6. 读真实文件并编码(如果存在)
        filename := "/tmp/test_base64.txt"
        os.WriteFile(filename, []byte("这是一个测试文件内容\n用于演示Base64编码"), 0644)
        content, err := os.ReadFile(filename)
        if err != nil {
                fmt.Printf("读文件失败: %v\n", err)
        } else {
                fileEncoded := base64.StdEncoding.EncodeToString(content)
                fmt.Printf("文件名: %s\n", filename)
                fmt.Printf("原始大小: %d 字节\n", len(content))
                fmt.Printf("编码大小: %d 字节\n", len(fileEncoded))
                fmt.Printf("膨胀率: %.1f%%\n", float64(len(fileEncoded)-len(content))/float64(len(content))*100)
                fmt.Printf("Base64: %s\n", fileEncoded)
        }
        os.Remove(filename)

        // 7. 错误处理 - 非法Base64
        invalidBase64 := "ThisIsNot@Valid#Base64!"
        _, err = base64.StdEncoding.DecodeString(invalidBase64)
        if err != nil {
                fmt.Printf("非法Base64 '%s' 解码错误: %v\n", invalidBase64, err)
        }

        // 8. 严格模式
        // 标准模式允许尾部bits非零
        loose := base64.StdEncoding
        strict := base64.StdEncoding.Strict()
        testData := "SGVsbG8=" // "Hello"
        _, err1 := loose.DecodeString(testData)
        _, err2 := strict.DecodeString(testData)
        fmt.Printf("标准模式解码 '%s': %v\n", testData, err1)
        fmt.Printf("严格模式解码 '%s': %v\n", testData, err2)

        // 9. 编解码长度计算
        for _, n := range []int{1, 2, 3, 4, 5, 6, 10, 100} {
                encLen := base64.StdEncoding.EncodedLen(n)
                decLen := base64.StdEncoding.DecodedLen(encLen)
                fmt.Printf("输入%3d字节 → 编码%4d字节 → 解码最多%3d字节\n", n, encLen, decLen)
        }
}