我们为什么要使用Base64?

维基百科说

Base64编码scheme通常用于需要对需要存储和传输的二进制数据进行编码以处理文本数据的介质。 这是为了确保数据在运输过程中保持完整无需修改。

但是不是数据总是以二进制forms存储/传输,因为我们的机器存储二进制文件的内存,这取决于你如何解释它? 因此,无论您将位模式010011010110000101101110编码为ASCII为Man还是在Base64中编码为TWFu ,您最终都将存储相同的位模式。

如果最终的编码是以零和1来表示的,而每一台机器和媒体都可以处理这些编码,那么数据是以ASCII还是Base64来表示,怎么样?

这是什么意思是“旨在处理文本数据的媒体”? 他们可以处理二进制=>他们可以处理任何事情。


谢谢大家,我想我现在明白了。

当我们发送数据时,我们不能确定数据将被解释为与我们预期的相同的格式。 所以我们发送双方都知道的某种格式的数据(比如Base64)。 这样,即使发送者和接收者以不同的方式解释相同的事物,但是因为他们对编码格式达成一致,数据也不会被错误地解释。

从马克·拜尔例子

如果我想发送

 Hello world! 

一种方法是像ASCII一样发送它

 72 101 108 108 111 10 119 111 114 108 100 33 

但是字节10可能不会被正确解释为另一端的换行符。 所以,我们使用ASCII的一个子集来对它进行编码

 83 71 86 115 98 71 56 115 67 110 100 118 99 109 120 107 73 61 61 

以相同数量的信息传输更多的数据为代价,即使接收者碰巧对字符集的其余部分有不同的解释,也能确保接收者能够以预定的方式解码数据。

你的第一个错误是认为ASCII编码和Base64编码是可以互换的。 他们不是。 它们被用于不同的目的。

  • 当您使用ASCII编码文本时,您将从一个文本string开始,并将其转换为一个字节序列。
  • 当您在Base64中编码数据时,您将从一系列字节开始,然后将其转换为文本string。

为了理解为什么Base64是必要的,我们需要一些计算历史。


计算机以二进制0和1进行通信 – 但人们通常希望与更丰富的表单数据(如文本或图像)进行通信。 为了在计算机之间传输数据,首先必须将其编码为0和1,然后再发送,然后再解码。 以文本为例,有许多不同的方法来执行此编码。 如果我们都可以同意一个单一的编码,那将会简单得多,但遗憾的是情况并非如此。

最初创build了许多不同的编码(例如Baudot编码 ),每个字符使用不同数量的位,直到最终ASCII成为每个字符7位的标准。 然而,大多数计算机将二进制数据以8位组成的字节进行存储,所以ASCII不适合传输这种types的数据。 有些系统甚至会抹掉最重要的一点。 此外,跨系统的行结束编码的差异意味着ASCII字符10和13有时也被修改。

为了解决这些问题,引入了Base64编码。 这使您可以将多个字节的字节编码为已知可安全发送而不会被破坏的字节(ASCII字母数字字符和几个符号)。 缺点是使用Base64编码消息增加了其长度 – 每3个字节的数据被编码为4个ASCII字符。

要可靠地发送文本,您可以使用您select的文本编码(例如UTF-8)将其编码为字节,然后Base64将生成的二进制数据编码为可安全发送以ASCII编码的文本string。 接收器将不得不扭转这个过程来恢复原始信息。 这当然要求接收者知道使用了哪些编码,并且这些信息通常需要分开发送。

从历史上看,它被用于在电子邮件服务器可能修改行尾的电子邮件中编码二进制数据。 一个更现代的例子是使用Base64编码将图像数据直接embedded到HTML源代码中 。 这里有必要对数据进行编码,以避免像'<'和'>'这样的字符被解释为标签。


这是一个工作的例子:

我希望发送两行文字信息

你好
世界!

如果我将它作为ASCII(或UTF-8)发送它将看起来像这样:

 72 101 108 108 111 10 119 111 114 108 100 33 

在某些系统中,字节10被破坏,因此我们可以将这些字节作为Base64string进行编码:

  SGVsbG8sCndvcmxkIQ == 

当使用ASCII编码时,看起来像这样:

 83 71 86 115 98 71 56 115 67 110 100 118 99 109 120 107 73 61 61 

这里的所有字节都是已知的安全字节,所以任何系统都不会破坏这个消息。 我可以发送这个,而不是我原来的消息,并让接收方反向进程恢复原来的消息。

为什么不看当前定义Base64的RFC ?

数据的基本编码在许多情况下用于存储或传输
可能由于遗留原因,环境中的数据仅限于US-ASCII [1] data.Base编码也可以在没有传统限制的新应用程序中使用,仅仅是因为它可以使用文本编辑器来操作对象。

过去,不同的应用程序有不同的要求,因此有时以稍微不同的方式实现基本编码。 今天,协议规范有时通常使用基本编码,特别是“base64”,没有精确的描述或参考。 多用途Internet邮件扩展(MIME)[4]通常用作base64的参考,而不考虑换行或非字母字符的后果。 本规范的目的是build立通用的字母表和编码考虑因素。 这将有希望减less其他文件中的歧义,从而提高互操作性。

Base64最初被devise为允许将二进制数据作为多用途Internet邮件扩展的一部分附加到电子邮件的一种方式。

假设你想在XML文档中embedded一对图像。 图像是二进制数据,而XML文档是文本。 但是XML不能处理embedded的二进制数据。 你是怎么做到的?

一种select是将图像编码为base64,将二进制数据转换为XML可以处理的文本。

代替:

 <images> <image name="Sally">{binary gibberish that breaks XML parsers}</image> <image name="Bobby">{binary gibberish that breaks XML parsers}</image> </images> 

你做:

 <images> <image name="Sally" encoding="base64">j23894uaiAJSD3234kljasjkSD...</image> <image name="Bobby" encoding="base64">Ja3k23JKasil3452AsdfjlksKsasKD...</image> </images> 

XMLparsing器将能够正确parsingXML文档并提取图像数据。

为文本数据devise的媒体当然最终也是二进制的,但是文本媒体通常使用某些二进制值作为控制字符。 此外,文本媒体可能会拒绝某些二进制值为非文本。

Base64编码将二进制数据编码为只能在文本媒体中解释为文本的值,并且没有任何特殊字符和/或控制字符,因此数据也将在文本媒体中保存。

媒体validationstring编码越多,所以我们希望确保数据可以被处理应用程序接受(并且不包含表示EOL的二进制序列)

想象一下,你想用一个编码为UTF-8的电子邮件发送二进制数据 – 如果1和0的数据stream在UTF-8编码中产生一个无效的Unicode 序列 ,电子邮件可能无法正确显示。

当我们想要对URL中的URL无效的字符进行编码时,URL中会出现同样的情况:

http://www.foo.com/hello我的朋友; – > http://www.foo.com/hello%20my%20friend

这是因为我们想要发送一个空间在系统上,认为这个空间是臭的。

我们所做的只是确保已知的好的,可接受的和无害的位序列与另一个字面序列位之间存在1对1的映射,并且处理应用程序不区分编码。

在你的例子中, man可能是第一种forms的有效ASCII码; 但通常情况下,您可能需要传输随机二进制值(即通过电子邮件发送图像):

MIME版本:1.0
内容描述:“a.gif的Base64编码”
Content-Type:image / gif; NAME = “A.GIF”
内容传输编码:Base64
内容处理:附件; 文件名= “A.GIF”

在这里,我们看到一个GIF图像在base64中被编码为一个电子邮件的块。 电子邮件客户端读取标题并对其进行解码。 由于编码,我们可以肯定GIF不包含任何可能被解释为协议的东西,我们避免插入SMTP或POP可能发现的重要数据。

当我发现它方便的一个例子是当试图embedded二进制数据在XML中 。 一些二进制数据被SAXparsing器误解,因为这些数据可能是字面上的任何东西,包括XML特殊字符。 Base64对发送端的数据进行编码并在接收端进行解码,解决了这个问题。

大多数计算机以8位二进制格式存储数据,但这不是要求。 有些机器和传输介质一次只能处理7位(甚至更小)。 这样的媒体将会以7比特的倍数来解释码stream,所以如果你发送8比特的数据,你将不会收到你期望的另一边的数据。 Base-64只是解决这个问题的一种方法:将input编码为6位格式,通过媒介发送并在接收端将其解码为8位格式。

这是什么意思是“旨在处理文本数据的媒体”?

这些协议的目的是处理文本(通常只有英文文本),而不是二进制数据(如.png和.jpg图像)。

他们可以处理二进制=>他们可以处理任何事情。

但相反是不正确的。 旨在表示文本的协议可能会错误地处理恰好包含以下内容的二进制数据:

  • 字节0x0A和0x0D用于行结尾,因平台而异。
  • 其他控制字符,如0x00(NULL = Cstring终止符),0x03(END OF TEXT),0x04(END OF TRANSMISSION),或0x1A(DOS文件结束符)可能会提前发出数据结束的信号。
  • 0x7F以上的字节(如果协议是为ASCIIdevise的)。
  • 字节序列是无效的UTF-8。

所以你不能通过基于文本的协议发送二进制数据。 你仅限于表示非空间非控制ASCII字符的字节,其中有94个。selectBase 64的原因是,处理两个幂的速度更快,而64是最大的工作。

一个问题,但。 那么这个系统又如何不同意像常见的UTF-8这样的通用编码技术呢?

至less在网上,他们大部分都有。 大多数网站使用UTF-8 。

在西方的问题是,有很多旧软件,即1字节= 1字符的ass-u-me-s,不能用UTF-8工作。

在东方的问题是他们对编码如GB2312和Shift_JIS的依赖。

事实上,微软似乎还没有得到select错误的UTF编码。 如果您要使用Windows API或Microsoft C运行时库,则仅限于UTF-16或区域设置的“ANSI”编码。 这使得使用UTF-8变得很痛苦,因为你必须一直转换。

这是什么意思是“旨在处理文本数据的媒体”?

早在ASCII规定世界处理非ASCII值的那一天,就令人头痛。 人们跳过各种各样的铁环,通过电线转移而不丢失信息。

除了其他的(有些冗长的)答案:即使忽略只支持7位ASCII的旧系统,在文本模式下提供二进制数据的基本问题是:

  • 换行符通常以文本模式进行转换。
  • 我们必须小心,不要将NUL字节视为文本string的结尾,这在任何具有C谱系的程序中都很容易实现。

我会给你一个非常不同但真实的例子:我写的JavaScript代码在浏览器中运行。 HTML标签具有ID值,但是在ID中有什么字符是有效的。

但是我想让我的ID无损地引用我的文件系统中的文件。 现实中的文件可以用惊叹号,重音字符,代字符,甚至表情符号来expression各种怪异的奇妙字符! 我不能做到这一点:

 <div id="/path/to/my_strangely_named_file!@().jpg"> <img src="http://myserver.com/path/to/my_strangely_named_file!@().jpg"> Here's a pic I took in Moscow. </div> 

假设我想运行这样的代码:

 # ERROR document.getElementById("/path/to/my_strangely_named_file!@().jpg"); 

我认为这段代码执行时会失败。

有了Base64,我可以参考一些复杂的东西,而不用担心哪种语言允许哪些特殊字符以及哪些需要转义:

 document.getElementById("18GerPD8fY4iTbNpC9hHNXNHyrDMampPLA"); 

与使用MD5或其他散列函数不同,您可以反转编码以找出实际有用的数据。

我希望我知道Base64几年前。