Base64 编码 URI/URL 详解一、Base64 是什么1.1 一句话定义Base64 是一种把二进制数据转换成纯文本的编码方式。用 64 个可打印字符字母、数字、、/来表示任意字节数据。1.2 为什么需要它计算机里很多数据是二进制的——图片、音频、加密密钥、Protobuf 消息等。但很多传输协议只支持文本协议/场景限制JSON只能传 UTF-8 文本不能传原始字节HTTP Header纯 ASCII二进制值会出错SMTP邮件最早只支持 7-bit ASCIIHTMLimgsrc必须是 URL 或 Data URIBase64 就是翻译器把机器读的字节翻成协议能传送的字符串。1.3 在我的项目里干什么用本地 test.png → [二进制 PNG 数据] → Base64 编码 → iVBORw0KGgo... ↓ 塞进 JSON body 发给火山引擎 API ↓ 服务器反向解码还原图片没有 Base64你就得先把图片上传到 OSS拿到 URL再把 URL 传给 API——多一步。Base64 让你直接把本地文件嵌入请求里。二、Base64 是怎么编出来的2.1 核心思想3 字节 → 4 字符原始: 3 个字节 24 个比特 Base64: 24 个比特 ÷ 6 4 个字符每个 Base64 字符代表 6 个比特0-63对应字符集里的一个位置。2.2 字符集64 个字符索引 字符 索引 字符 索引 字符 索引 字符 0 A 16 Q 32 g 48 w 1 B 17 R 33 h 49 x 2 C 18 S 34 i 50 y 3 D 19 T 35 j 51 z 4 E 20 U 36 k 52 0 5 F 21 V 37 l 53 1 6 G 22 W 38 m 54 2 7 H 23 X 39 n 55 3 8 I 24 Y 40 o 56 4 9 J 25 Z 41 p 57 5 10 K 26 a 42 q 58 6 11 L 27 b 43 r 59 7 12 M 28 c 44 s 60 8 13 N 29 d 45 t 61 9 14 O 30 e 46 u 62 15 P 31 f 47 v 63 /2.3 手动编码示例把Man三个字符编成 Base64步骤 1: 取每个字符的 ASCII 码 M → 77 (0x4D) → 01001101 a → 97 (0x61) → 01100001 n → 110 (0x6E) → 01101110 步骤 2: 拼成 24 位二进制流 01001101 01100001 01101110 步骤 3: 切成 4 个 6 位 010011 010110 000101 101110 ↓ ↓ ↓ ↓ 步骤 4: 每 6 位转成十进制 (0-63) 010011 → 19 010110 → 22 000101 → 5 101110 → 46 步骤 5: 查字符集 19 → T 22 → W 5 → F 46 → u 结果: TWFu2.4 填充规则号当原始字节数不是 3 的倍数时剩余字节数填充1 字节8 位取 6 位 填 2 个零 → 2 个 Base64 字符 2 字节16 位取 12 位 → 3 个 Base64 字符 3 字节24 位刚好 4 个字符不填示例只编码一个字节M(77) → 01001101010011 01.... ← 只有 8 位后 4 位不存在 010011 010000 ← 后面填 0000 凑够 12 位 ↓ ↓ 19 16 T Q 结果: TQ 两个 表示原始数据只有 1 字节解码时忽略填充位三、URI 和 URL 的关系3.1 一句话URL 是 URI 的一种。URI 是标识某个资源URL 在 URI 的基础上多了怎么获取它。3.2 层级关系URI (Uniform Resource Identifier - 统一资源标识符) ├── URL (Uniform Resource Locator - 统一资源定位符) │ 这个资源在哪里、怎么拿到 │ 例: https://example.com/photo.png │ 文件::///home/user/photo.png │ ftp://server/file.zip │ └── URN (Uniform Resource Name - 统一资源名称) 这个资源叫什么名字但不告诉你它在哪 例: urn:isbn:0-486-27557-4 (一本书的 ISBN) urn:uuid:6e69285a-1461-459e... (一个 UUID)3.3 关键区别URIURLURN概念“资源叫这个”“去这里拿”“它就叫这个名字”是否包含位置不一定必须不包含能直接访问吗不一定能不能例子https://a.com/x.png✓✗data:image/png;base64,...✗✗urn:isbn:0451450523✗✓mailto:hiexample.com✗✗所有 URL 都是 URI但不是所有 URI 都是 URL。3.4 Data URI 的特殊位置Data URI 是最特殊的一类 URIdata:image/png;base64,iVBORw0...它是 URI唯一标识了一个资源这张图片它不是 URL没有告诉你去哪里下载资源就在字符串本身里它不是 URN没有给资源命名而是直接附带了完整数据可以理解为自包含的 URI——资源即是标识符标识符即是资源。3.5 你项目里两个 URI 的对比// 请求中告诉 API 参考图长这样data:image/png;base64,iVBORw0KGgo...// Data URI (非 URL)// ↑ 图片数据直接嵌在字符串里// 响应中API 返回 生成结果在这里https://ark-volces.com/tmp/abc123.png// URL (也是 URI)// ↑ 指向远程服务器上的一个文件四、C 中实现 Base64 编码4.1 完整实现零依赖~15 行conststd::string BASE64_CHARSABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/;std::stringbase64_encode(constunsignedchar*data,size_t len){std::string result;result.reserve(4*((len2)/3));// 预分配避免反复扩容for(size_t i0;ilen;i3){// 把最多 3 个字节拼成 24 位整数unsignedintn(unsignedint)data[i]16;if(i1len)n|(unsignedint)data[i1]8;if(i2len)n|(unsignedint)data[i2];// 24 位切成 4 个 6 位查表得到 4 个字符result.push_back(BASE64_CHARS[(n18)0x3F]);// 位[23:18]result.push_back(BASE64_CHARS[(n12)0x3F]);// 位[17:12]result.push_back((i1len)?BASE64_CHARS[(n6)0x3F]// 位[11:6]:);// 填充result.push_back((i2len)?BASE64_CHARS[n0x3F]// 位[5:0]:);// 填充}returnresult;}4.2 逐行解释预分配result.reserve(4*((len2)/3));len(len2)/3× 4含义1141 字节 → 4 字符含2142 字节 → 4 字符含3143 字节 → 4 字符4284 字节 → 8 字符10034136100 字节 → 136 字符(len2)/3是ceil(len/3)的整除技巧。24 位拼接unsignedintn(unsignedint)data[i]16;if(i1len)n|(unsignedint)data[i1]8;if(i2len)n|(unsignedint)data[i2];位[23:16] ← data[i] 位[15:8] ← data[i1] (如果存在) 位[7:0] ← data[i2] (如果存在)6 位切片 查表24 位: [23:18] [17:12] [11:6] [5:0] ↓ ↓ ↓ ↓ 操作: 18 12 6 0 掩码: 0x3F 0x3F 0x3F 0x3F18 0x3F意思是向右移 18 位只保留最低 6 位。0x3F00111111二进制 63十进制刚好取低 6 位。条件填充(i1len)?BASE64_CHARS[...]:原始第 2 个字节不存在 → 输出原始第 3 个字节不存在 → 输出解码时看到就知道这轮只有 1 或 2 个真实字节4.3 解码实现对称反向std::vectorunsignedcharbase64_decode(conststd::stringencoded){// 构建反向查找表: 字符 → 6 位值intreverse[256]{};for(inti0;i64;i){reverse[(unsignedchar)BASE64_CHARS[i]]i;}std::vectorunsignedcharresult;result.reserve(encoded.size()*3/4);intval0,bits-8;for(unsignedcharc:encoded){if(c)break;// 遇到 停止val(val6)|reverse[c];// 每 6 位拼进 24 位缓冲区bits6;if(bits0){result.push_back((valbits)0xFF);// 取出一个完整字节bits-8;}}returnresult;}五、Base64 的主要用途5.1 用途总览场景举例API 传图片/文件图生图 API 的image字段就是你的项目Data URI 内嵌HTMLimg srcdata:image/png;base64,...邮件附件MIMEContent-Transfer-Encoding: base64JWT TokeneyJhbGci...的 header/payload 部分密钥存储SSH 公钥-----BEGIN PUBLIC KEY-----URL 安全传输二进制 ID 转成 URL-safe 字符串/→-_配置文件k8s Secret 里的证书、docker registry 认证5.2 为什么不用 Base16 (hex)Base16Base64开销膨胀 100%1 字节 → 2 字符膨胀 33%3 字节 → 4 字符示例FF00AB12.../wCrEg...优点人眼直接可读更紧凑Base64 是可读性和体积之间的最优折中。5.3 常见变体变体区别用途标准 Base64用/填充JSON、XML、一般场景URL-Safe Base64/→-_去掉URL 参数、文件名、JWTBase64 without padding省略尾部节省几个字符六、一张图总结┌─────────────────────────────────────────────────────────┐ │ URI (标识资源) │ │ ┌──────────────────────┐ ┌──────────────┐ │ │ │ URL (定位资源) │ │ URN (命名资源)│ │ │ │ │ │ │ │ │ │ https://x.com/a.png │ │ urn:isbn:123 │ │ │ │ file:///home/a.png │ │ │ │ │ └──────────────────────┘ └──────────────┘ │ │ │ │ ┌──────────────────────────┐ │ │ │ Data URI (自包含资源) │ 不属于 URL 也不属于 URN │ │ │ │ │ │ │ data:image/png;base64, │ │ │ │ iVBORw0KGgoAAAA... │ │ │ │ ─────────── ────────── │ │ │ │ 告诉服务器: 实际图片 │ │ │ │ 这是PNG 数据编码 │ │ │ └──────────────────────────┘ │ └─────────────────────────────────────────────────────────┘ Base64 编码过程: 原始图片 (二进制) → [3字节一组] → [4字符] → JSON 文本 ↓ ↓ ↓ ↓ 无法直接塞 JSON 切成 24 位 查表映射 拼进 body iVBORw0...