首页 > 其他分享 >探索Base64奥秘:数据转换的神奇魔法师

探索Base64奥秘:数据转换的神奇魔法师

时间:2024-09-23 10:21:28浏览次数:3  
标签:字符 字节 编码 二进制 Base64 魔法师 奥秘 数据

一、简介

​ Base64是一种基于64个可打印ASCII字符来表示二进制数据的编码方式,常用于需要通过文本数据传输、存储二进制数据的场景中。例如在前端开发中的使用场景有:在HTML和CSS中将图像文件转为Base64数据嵌入到文档中、在前后端JSON数据交互时,将文件转为Base64数据进行上传等等。

​ 标准的Base64所使用的64个可打印ASCII字符,由大写字母 A-Z、小写字母 a-z、数字 0-9 以及两个特殊字符 +/组成。

标准Base64映射关系表:

在这里插入图片描述

​ 简单总结一下映射关系:6位的二进制数据,可以对应的转换为十进制的:063,025映射的是大写字母 A-Z,26~51映射的是小写字母 a-z,52~61映射的是数字 0-9,62映射字符+,63映射字符/

​ 但在某些特殊场景下,会使用一种变体Base64,被称为 “URL安全的Base64” ,其用-_代替了+/,避免字符特殊含义所导致的问题,并省略了填充字符=。因为在URL中,+通常表示空格,而/是路径分隔符,这些字符可能会导致URL解析错误。

二、编码和解码

1、编码过程

  • 转换分组: 将需要编码的数据转换为二进制数据格式,然后按照每3个字节(24位)为一组进行分组。最后一组可能不足 3 个字节,缺失1个或2个字节。
  • 分割: 将每组24位的数据按照每6位再次进行分割,分割为4个6位的二进制数据块。如果最后一组不足 3 个字节,在分割时,按照每6位进行分割,可能会被分割为2个或3个二进制数据块,且最后一个二进制数据块可能不足6位,需要用0去填充,确保最后一个二进制数据块也是6位。
  • 编码: 将每个6位的二进制数据块根据Base64字符集的映射关系,找到编码后对应的字符,每组3个字节对应编码后的4个字符,不足3个字节的组,根据分割的二进制数据块,编码后会对应2个或3个字符。
  • 填充: 如果在第一步分组时,如果最后一组不缺失字节,则无需填充;如果最后一组不足三个字节,则缺几个字节,就需要在编码后的字符串末尾添加几个等号(=),确保最终的编码结果是4的倍数。换句话说,如果原始数据的字节数除以 3 余 1,那么在编码后的字符串末尾添加两个等号(=);如果原始数据的字节数除以 3 余 2,那么在编码后的字符串末尾添加一个等号(=)。
具体案例:

​ 以字符串"Man"为例,我们要对其进行Base64编码,第一步需要进行转换分组:将字符串中的3个字符转换为对应的ASCII码字节表示(三个字符对应三个字节),然后再将ASCII码转换为8位二进制数据,二进制数据连接起来,并按照每3个字节(24位)为一组进行分组。

// 原始字符串
"Man"
// 包含三个字符
"M" "a" "n"
// 转换为对应的ASCII码
77 97 110
// 再转换为对应的8位二进制数据
01001101 01100001 01101110
// 连接起来正好为一个24位的二进制数据组
010011010110000101101110	

​ 第二步进行分割:将每组24位的数据按照每6位再次进行分割,分割为4个6位的二进制数据块。此时只有一组数据,且正好满足3个字节。

// 按照每6位 进行分割
010011 010110 000101 101110

​ 第三步进行编码:将每个二进制数据块,根据Base64字符集的映射关系,找到对应的字符。

// 对应十进制
19 22 5 46
// 对应字符
T W F u

​ 第四步进行填充:由于原始数据"Man"的字节数量正好为3的倍数,因此不需要填充,第三步的结果即为最终结果。

// 最终Base64编码结果
Man -> TWFu

​ 如果以字符串"Woman"为例,我们要对其进行Base64编码,第一步需要进行转换分组:将字符串中的5个字符转换为对应的ASCII码字节表示,然后再将ASCII码转换为8位二进制数据,二进制数据连接起来,并按照每3个字节(24位)为一组进行分组。

// 原始字符串
"Woman"
// 包含五个字符
"W" "o" "m" "a" "n"
// 转换为对应的ASCII码
87 111 109 97 110
// 再转换为对应的8位二进制数据
01010111 01101111 01101101 01100001 01101110
// 连接起来成为一个二进制数据组
0101011101101111011011010110000101101110
// 按照每3个字节(24位)为一组进行分组 分成两组 第二组缺失1个字节(8位)
010101110110111101101101 0110000101101110

​ 第二步进行分割:将每组24位的数据按照每6位再次进行分割,分割为4个6位的二进制数据块。由于第二组只有2个字节(16位),因此在切割后,最后一个数据块只有4位,需要用0去填充,使最后一个二进制数据块也是6位。

// 每组24位的数据按照每6位再次进行分割 分割为4个6位的二进制数据块
// 第一组
010101 110110 111101 101101 
// 第二组 由于只有16位 在切割后 最后一个数据块只有4位 不足6位,需要用0去填充
011000 010110 111000

​ 第三步进行编码:将每个二进制数据块,根据Base64字符集的映射关系,找到对应的字符。

// 第一组 对应十进制
21 54 61 45
// 对应字符
V 2 9 t

// 第二组 对应十进制
24 22 56
// 对应字符
Y W 4

​ 第四步进行填充:由于原始数据"Woman"的字节数量除以 3 余 2,因此需要在第三步编码后的字符串末尾添加一个等号(=)。

// 第三步编码结果
V29tYW4
// 填充等号
V29tYW4=
// 最终Base64编码结果
Woman -> V29tYW4=

2、解码过程

  • 解码: 将Base64编码字符串中的每个字符按照Base64字符集的映射关系,进行解码,将每个字符转换为对应的6位二进制数据块,连接起来。并查看最后是否存在填充字符=,如果存在则将其删除。
  • 分割: 将连接起来的二进制数据按照每8位进行分割,分割成8位的二进制数据块,一个二进制数据块对应1个字节。如果二进制数据无法被8整除,则说明编码时进行了填充0位,只需从前向后正常进行分割,最终将多余的0位舍弃即可。
  • 转换: 将每一个8位二进制数据块转换为对应原始字符,最终所有原始字符拼接后就能得到原始数据。
具体案例:

​ 如果以Base64编码字符串“TWFu”为例,我们对其进行解码操作,第一步进行解码:将每个字符按照Base64字符集的映射关系,进行解码,并将对应的6位二进制数据连接起来。

// Base64编码字符串 
TWFu
// 解码对应的十进制
19 22 5 46
// 对应的4个6位二进制数据块
010011 010110 000101 101110
// 进行连接 得到24位二进制数据
010011010110000101101110

​ 第二步进行分割:将得到的二进制数据 按照每8位进行分割,得到8位的二进制数据块。

// 按照每8位进行分割
01001101 01100001 01101110

​ 第三步转换:将8位的二进制数据,按照ASCII码转换为对应原始字符,所有原始字符拼接后就能得到原始数据。

// 按照ASCII码转换为对应原始字符
M a n
// 连接得到原始数据
Man

​ 如果以Base64编码字符串“V29tYW4=”为例,我们对其进行解码操作,第一步进行解码:将每个字符按照Base64字符集的映射关系,进行解码,并将对应的6位二进制数据连接起来。此时最后存在一个填充字符=,将其删除。

// Base64编码字符串
V29tYW4=
// 解码对应的十进制 忽略等号
21 54 61 45 24 22 56
// 对应的6位二进制数据块
010101 110110 111101 101101 011000 010110 111000
// 进行连接
010101110110111101101101011000010110111000

​ 第二步进行分割:将得到的二进制数据 按照每8位进行分割,得到8位的二进制数据块。但此时,你会发现无法被8整除,最终会多余3个二进制位 000 ,是编码时填充的0位,将其舍弃。

// 按照每8位进行重新分割 将多余的填充位舍弃
01010111 01101111 01101101 01100001 01101110

​ 第三步转换:将8位的二进制数据,按照ASCII码转换为对应原始字符,所有原始字符拼接后就能得到原始数据。

// 按照ASCII码转换为对应原始字符
W o m a n
// 连接得到原始数据
Woman

三、优点和缺点

1、优点

  • 兼容性好: Base64编码后的数据中只包含64个可打印字符的组合,可以在各种文本环境中进行传输和存储。而且Base64 是一种标准的编码方式,几乎所有的编程语言和平台都支持它,更有诸多现成的库和工具可以使用。
  • 无损编码: Base64编码属于无损编码的一种,编码和解码过程不会丢失任何信息。
  • 前端优化: 在前端页面开发中,将小图片使用Base64编码表示,可以减少页面的请求数量,进行性能优化等等。

2、缺点

  • 体积膨胀: 由于每3个字节的原始数据会被编码为4个字符,因此Base64编码的数据大约要比原始数据体积增加33.3%。
  • 安全性有限:Base64编码虽然可以隐藏数据的原始内容,避免被直接读取,但是其并不属于加密算法,很容易地就被解码回原始数据,因此不适合在敏感信息上使用。
  • 资源消耗: Base64编码和解码的过程需要一定的计算,如果是对大量数据进行处理,则需要消耗大量的CPU等系统资源。

四、JavaScript支持

1、bota()

​ 在Window对象上,提供了btoa()方法,用于将一个二进制字符串按照Base64进行编码。但是该方法只能处理 ASCII 字符,如英文字母、英文符号和数字等,不能对中文字符和中文符号进行编码处理。

// 数字
window.btoa('123');   // MTIz
// 英文字母
window.btoa('ABCabc'); // QUJDYWJj
// 英文符号
window.btoa(',.?:+-'); // LC4/Oist
// 中文字符
window.btoa('猪猪侠'); // 报错
// 中文符号
window.btoa(',。'); // 报错

​ 如果想要支持对中文字符和中文符号的编码,则需要结合encodeURIComponent()方法实现,在进行Bas64编码之前,需要将中文字符通过encodeURIComponent()方法进行处理,该方法会将中文字符转换为 UTF-8 编码的字节序列,并将这些字节序列表示为百分号转义形式。此时的百分号转义序列中,只包含了ASCII 字符,自然也就可以被btoa()方法所编码。

let str = '猪猪侠';
str = window.encodeURIComponent(str); // %E7%8C%AA%E7%8C%AA%E4%BE%A0
window.bota(str); // JUU3JThDJUFBJUU3JThDJUFBJUU0JUJFJUEw
2、atob()

​ 在Window对象上,提供了atob()方法,用于将一个Base64编码字符串进行解码,将其恢复为原始数据。

window.atob('MTIz');   // 123
window.atob('QUJDYWJj'); // ABCabc
window.atob('LC4/Oist'); // ,.?:+-

​ 但如果Base64编码字符串中包含了encodeURIComponent()方法处理过的字符,想要获取真正的原始数据,还需要再通过decodeURIComponent()方法,将atob()方法解码后的数据进行二次还原。

window.atob('JUU3JThDJUFBJUU3JThDJUFBJUU0JUJFJUEw'); // %E7%8C%AA%E7%8C%AA%E4%BE%A0
window.decodeURIComponent('%E7%8C%AA%E7%8C%AA%E4%BE%A0'); // 猪猪侠
3、FileReader

FileReader对象可以异步的读取用户计算机上文件,但仅限于以安全的方式(<input>DataTransfer等方式)读取FileBlob类型的文件。读取文件之后,可以通过FileReader.readAsDataURL()方法,将文件数据转换为包含data:URL的base64编码数据。

// 工具函数
const fileToBase64 = (file: File): Promise<{ status: boolean, data?: any, error?: any }> => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    
    // 转换成功
    reader.onload = () => {
      resolve({
        status: true,
        data: reader.result
      });
    };
    
    // 转换失败
    reader.onerror = () => {
      reject({
        status: false,
        error: reader.error
      });
    };
  });
};

请关注公众号,查看更多优质资源:

标签:字符,字节,编码,二进制,Base64,魔法师,奥秘,数据
From: https://blog.csdn.net/weixin_45092437/article/details/142316757

相关文章

  • C# base64转pdf + 上传至指定url
    base64topdf1usingSystem;2usingSystem.Collections.Generic;3usingSystem.IO;4usingSystem.Linq;5usingSystem.Text;6usingSystem.Threading.Tasks;78namespaceHS.Common.Helper9{10publicclassPdfHelper11{......
  • Nginx 中 proxy_pass 末尾斜杠的奥秘
    一、proxy_pass的类型概述Nginx的官网将proxy_pass分为两种类型:不带URI方式和带URI方式。不带URI方式只包含IP和端口号,例如proxy_passhttp://localhost:8080。而带URI方式在端口号之后有其他路径,包括只有单个“/”的,如proxy_passhttp://localhost:8080/,以及其......
  • Go语言基础-常见编码(Json、Base64)
    编码jsonjson是go标准库里自带的序列化工具,使用了反射,效率比较低easyjson值针对预先定义好的json结构体对输入的json字符串进行纯字符串的截取,并将对应的json字段赋值给结构体easyjson-allxxx.go生成go文件中定义的结构体对应的解析xxx.go所在的package不能是mainfunce......
  • 服务API接口:解锁API接口参数的奥秘
    在软件开发中,API接口参数是构建有效通信的核心。它们定义了API调用的方式和数据交换的规则,对于确保API的功能性和可用性至关重要。本文将深入探讨服务API接口中的参数,揭示如何通过精心设计的参数提升API的性能和用户体验。什么是API接口参数?API接口参数是在调用API时传递给API的数......
  • 深入探索:深度优先遍历与广度优先遍历的奥秘与应用
    在算法和数据结构的广阔领域中,图的遍历是一个核心且基础的概念,它支撑着众多高级算法和应用的实现。深度优先遍历(DFS)和广度优先遍历(BFS)作为图的两种基本遍历方式,不仅具有深刻的理论意义,还广泛应用于各种实际问题中。本文将更深入地探讨这两种遍历方式的原理、实现细节、性能......
  • base64
    importjava.nio.charset.StandardCharsets;importjava.util.Base64;publicclassBase64Example{publicstaticvoidmain(String[]args){StringoriginalString="oa:123456";//使用Base64编码器Base64.Encoderenc......
  • vulnhub(9):sickos1.2(深挖靶机的各个细节、文件管道反弹shell详解、base64编码反弹shell
    端口nmap主机发现nmap-sn192.168.148.0/24​Nmapscanreportfor192.168.148.131Hostisup(0.00020slatency).​131是新出现的机器,他就是靶机nmap端口扫描nmap-Pn192.168.148.131-p---min-rate10000-oAnmap/scan扫描开放端口保存到nmap/scan下​......
  • Python中的“秘密武器”:成员运算符的奥秘与妙用
    在Python编程的世界里,成员运算符就像是隐藏在背后的超级英雄,它们虽然不像循环或条件判断那样经常出现在舞台中央,但却在构建高效、简洁的代码时扮演着至关重要的角色。今天,让我们一起揭开成员运算符的神秘面纱,探索它如何帮助我们解决实际问题,并让我们的代码更加优雅。引言......
  • 探索Kubernetes服务发现与Ingress的奥秘
    在云原生架构的深邃海洋中,第32天我们扬帆起航,探索Kubernetes服务发现与Ingress的广阔天地。这两项技术如同航海中的灯塔与航道,为Pod间的通信及外部访问提供了可靠的导航。服务发现:Pod间的隐形桥梁在Kubernetes集群中,服务发现机制允许Pod通过服务(Service)这一抽象层相互发现并进行通......
  • Java 中图片转换为 Base64
    importjava.io.File;importjava.io.FileInputStream;importjava.io.IOException;importjava.util.Base64;publicclassImageToBase64Converter{publicstaticvoidmain(String[]args){//指定要转换的图片路径StringimagePath="path......