在我们项目开发中,Base64想必大家都不会很陌生,Base64是将「二进制数据」转换为文本的一种优雅方式,使存储和传输变得容易。但是,作为一个合格的程序员,我们应该有一种打破砂锅问到底的求助欲望。
所以,今天我们来讲讲在各种语言中出镜率都高的离谱的Base64算法。今天,我们就用我们在初高中语文老师教我们的描述一个事物的三大步骤:1. 是什么,2. 如何工作,3. 为什么它很重要。来讲讲Base64算法。
好了,天不早了,干点正事哇。
「前置知识点」,只是做一个概念的介绍,不会做深度解释。因为,这些概念在下面文章中会有出现,为了让行文更加的顺畅,所以将本该在文内的概念解释放到前面来。「如果大家对这些概念熟悉,可以直接忽略」同时,由于阅读我文章的群体有很多,所以有些知识点可能「我视之若珍宝,尔视只如草芥,弃之如敝履」。以下知识点,请「酌情使用」。
RFC,全称为Request for Comments,是一种用于定义「互联网标准和协议」的文件系列。
RFC最早由互联网工程任务组(IETF)创建,用于记录和传播互联网协议、方法和最佳实践的提案、规范和讨论。
「每个 RFC 都有一个唯一的编号」,通常以RFC开头,后面跟着一个数字,例如RFC 791、RFC 2616等。RFC文档通常包含了协议规范、技术说明、最佳实践、标准化提案等,以促进互联网技术的发展和互操作性。
我们可以在IETF-datatracker[1]中输入指定的编号或者查找的关键字进行搜寻。
图片
以下是一些常见的RFC文档,大家可以翻阅自己想了解的技术点:
Latin-1,也称为ISO-8859-1,是一种由国际标准化组织(ISO)认可的「8 位字符集」,代表了「西欧语言的字母表」。正如其名称所示,「它是ISO-8859的一个子集」,该标准还包括用于写作系统如西里尔文、希伯来文和阿拉伯文的其他相关字符集。它被大多数Unix系统以及Windows系统使用。
Latin-1有时被不太准确地称为「扩展 ASCII」。
这是因为其字符集的前 128 个字符与美国 ASCII 标准相同。其余字符集包含了带重音的字符和符号。
关于更详细的Latin-1的表格,可以参考Latin-1-table[3]
btoa 是 JavaScript 中的一个内置函数,用于将二进制数据(通常是 8 位字节)编码为 Base64 字符串。它的名称是 binary to ASCII 的缩写,用于将二进制数据转换为文本字符串,以便在文本协议中传输或存储。
btoa 函数接受一个字符串参数,该字符串包含二进制数据。它将该二进制数据转换为 Base64 编码的字符串。
const binaryData = "front789";const base64String = btoa(binaryData);console.log(base64String);
这段代码将 front789 这个字符串转换为 Base64 编码的字符串并将结果打印到控制台。
尽管 btoa 是一个有用的函数,但它有一些限制:
Data URL 是一种统一资源标识符(URI)方案,用于将数据嵌入到文档中,而不是从外部文件加载数据。Data URL 允许我们将数据(如文本、图像、音频等)直接包含在网页或文档中,而不需要额外的 HTTP 请求。这种方式对于小型资源或需要避免外部请求的情况非常有用。
Data URL 的基本结构如下:
data:[<mediatype>][;base64],<data>
其中:
以下是 Data URL 的一些常见用途和示例:
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/wAABgAB/OGirwAAAABJRU5ErkJggg==" alt="Embedded Image"/>
<style> body { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/wAABgAB/OGirwAAAABJRU5ErkJggg==); }</style>
@font-face { font-family: "CustomFont"; src: url(data:application/font-woff;base64,d09GRgABAAAA...) format("woff");}
<script> let greeting = "前端柒八九"; alert(greeting);</script>
要理解为什么需要 Base64 编码,我们需要了解一些计算机历史。
计算机以二进制(0 和 1)进行通信,但人们通常希望使用更丰富的数据形式进行通信,如文本或图像。「为了在计算机之间传输数据,首先必须将其编码为 0 和 1,然后再解码」。以文本为例,有许多不同的编码方式。如果我们都能就一个单一的编码方式达成一致,那将会简单得多,但很遗憾,这并不是事实。针对这块的内容,可以参考了不起的 Unicode
最初创建了许多不同的编码方式(例如 Baudot 编码),每种方式「使用不同数量的比特来表示一个字符」,直到最终 ASCII 成为一个标准,「每个字符使用 7 位」。然而,大多数「计算机将二进制数据存储为每个字节由 8 位组成的数据」,因此 ASCII 不适合传输这种类型的数据。一些系统甚至会删除最高位。
为解决这些问题,引入了 Base64 编码。这允许我们「将任意字节编码为已知不会损坏的字节」(ASCII 字母数字字符和一些符号)。缺点是使用 Base64 对消息进行编码会增加其长度 - 「每 3 个字节的数据编码为 4 个 ASCII 字符」。
要可靠地发送文本,我们可以首先使用自己选择的文本编码(例如 UTF-8)将其编码为字节,然后将结果的二进制数据使用 Base64 编码为可安全传输的 ASCII 文本字符串。接收者反转此过程以恢复原始消息。当然,这需要接收者知道使用了哪种编码,通常需要单独发送这些信息。
我们来看一个示例:
我希望发送一个带有两行的文本消息:
Helloworld!
如果我将其发送为 ASCII(或 UTF-8),它将如下所示:
72 101 108 108 111 10 119 111 114 108 100 33
某些系统会破坏字节 10,所以我们可以将这些字节作为 Base64 字符串进行 Base64 编码:
SGVsbG8Kd29ybGQh
这里的所有字节都是已知的安全字节,所以很少有机会使任何系统损坏此消息。我可以发送这个消息而不是我的原始消息,然后让接收者反转此过程以恢复原始消息。
Base64编码将二进制数据转换为文本,具体来说是ASCII文本。生成的文本仅包含A-Z、a-z、0-9以及符号+和/这些字符。
而在之前我们在了不起的 Unicode中介绍过ASCII的。
由于字母表中有 26 个字母,我们有26 + 26 + 10 + 2(64)个字符。因此,这种编码被命名为Base64。这 64 个字符被认为是「安全」的,也就是说,与字符<、>、/n等不同,「它们不会被旧计算机和程序误解」。
下面是经过 Base64 编码的文本front789的样子:ZnJvbnQ3ODk=。
还有一点需要注意,如果在使用JS对某一个文本进行准换时,如果该文本包含非Latin1字符的字符串,会报错,所以我们需要对其进行准换处理。
// 原始文本字符串,包含非Latin1字符const text = "前端柒八九";// 创建一个 TextEncoder 对象,用于将文本编码为字节数组const encoder = new TextEncoder();// 使用 TextEncoder 对象将文本编码为字节数组const data = encoder.encode(text);// 使用 String.fromCharCode 和展开运算符 (...) 将字节数组转换为字符串// 然后使用 btoa 函数将字符串转换为 Base64 编码const base64 = btoa(String.fromCharCode(...data));// 打印 Base64 编码后的结果console.log(base64); //5YmN56uv5p+S5YWr5Lmd
我们在这里并没有加密文本。给定Base64编码的数据,非常容易将其转换回(解码)原始文本。我们「只是改变了数据的表示」,即编码。
在本质上,Base64编码使用一组特定的、减少的字符来「编码二进制数据」,以防止数据损坏。
Base64字母表
由于只有64个字符可用于编码,我们可以仅使用6位来表示它们,因为2^6 = 64。每个Base64数字表示6位数据。一个字节中有8位,而 8 和 6 的「最小公倍数」是 24。因此,「24 位,或 3 个字节,可以用四个 6 位的 Base64 数字表示」。
我们可能在HTML文档中使用了<img src="789.jpeg">标签来包含图像。其实,我们可以直接将「图像数据」嵌入到 HTML 中,而不必使用外链!数据URL可以做到这一点,它们使用Base64编码的文本来内联嵌入文件。
<img src="data:image/gif;base64,xxxxbase64encodedtextxxxx" />data:[<mime type >][;charset=<charset>][;base64],<encoded data></encoded></charset></mime>
另一个常见的用例是当我们需要在网络上传输或存储一些二进制数据,而网络只能处理文本或ASCII数据时。这确保了数据在传输过程中保持不变。还有就是在 URL 中传递数据时,当数据包含不适合 URL 的字符时,此时Base64就有了用武之地。
Base编码还在许多应用程序中使用,因为它使得可以使用文本编辑器来操作对象。
我们还可以使用 Base64 编码「将文件作为文本传输」。
以下是将一些文本转换为 Base64 的简单算法。
通过上述操作我们会得到一个Base64编码的字符串。如果最后一组中的比特位不足,可以使用=或==作为填充。
让我们以front7作为范例,来模拟上述操作。
01100110 01110010 01101111 01101110 01110100 00110111f r o n t 7
011001 100111 001001 101111 011011 100111 010000 110111
011001 100111 001001 101111 011011 100111 010000 11011125 23 9 47 27 23 16 27
25 23 9 47 27 23 16 27Z n J v b n Q 3
然后我们完成了。名字front7在 Base64 中表示为ZnJvbnQ3。
乍一看,Base64 编码的好处并不是很明显。
想象一下,如果我们有一张图片或一个「敏感文件」(PDF、文本、视频等),而不是简单的字符串,我们想将它存储为文本。我们可以首先将其转换为二进制,然后进行 Base64 编码,以获得相应的 ASCII 文本。
现在我们可以将该文本发送或存储在任何地方,以任何我们喜欢的方式,而不必担心一些旧设备、协议或软件会错误解释原始二进制数据以损坏我们的文件。
所有编程语言都支持将数据编码为 Base64 格式以及从 Base64 格式解码数据。
// 简单字符串const text1 = "front789";bota(text1); // ZnJvbnQ3ODk=// 超出`Latin-1`字符的字符串const text2 = "前端柒八九";const encoder = new TextEncoder();const data = encoder.encode(text);const base64 = btoa(String.fromCharCode(...data));console.log(base64); //5YmN56uv5p+S5YWr5Lmd
用Rust的话,我们可以直接用 base64 crate。
在 Cargo.toml 文件中添加以下内容:
[dependencies]base64 = "0.21.5"
use base64::{Engine as _, engine::general_purpose};let orig = b"data";let encoded: String = general_purpose::STANDARD_NO_PAD.encode(orig);assert_eq!("ZGF0YQ", encoded);assert_eq!(orig.as_slice(), &general_purpose::STANDARD_NO_PAD.decode(encoded).unwrap());// or, URL-safelet encoded_url = general_purpose::URL_SAFE_NO_PAD.encode(orig);
想了解更多关于Rust如何处理Base64,可以查看Rust base64[5]
此外,终端也内置支持 Base64 编码。在终端中尝试以下命令:
echo "前端柒八九" | base645YmN56uv5p+S5YWr5LmdCg==$ echo "5YmN56uv5p+S5YWr5LmdCg==" | base64 -d前端柒八九
本文链接:http://www.28at.com/showinfo-26-17389-0.html了不起的Base64
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com