我们为什么要用 Base64?

维基百科说

当需要编码二进制数据时,通常使用 Base64编码模式,这些二进制数据需要通过设计用于处理文本数据的媒体进行存储和传输。这是为了确保数据在传输过程中保持完整,无需修改。

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

如果最终的编码是0和1,每台机器和媒体都可以处理它们,那么数据表示为 ASCII 或 Base64又有什么关系呢?

“处理文本数据的媒体”是什么意思?它们可以处理二进制 = > 它们可以处理任何东西。


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

当我们发送数据时,我们不能确定数据是否会按照预期的格式进行解释。因此,我们以双方都能理解的格式(如 Base64)发送编码的数据。这样,即使发送方和接收方解释相同的东西不同,但因为他们在编码格式上达成一致,数据不会得到错误的解释。

来自 马克拜尔斯的例子

如果我想发送

Hello
world!

一种方法是像

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

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

140497 次浏览

“设计用于处理文本数据的媒体”是什么意思?

在ASCII码统治世界的时候,处理非ASCII码的值是一件令人头痛的事情。为了在不丢失信息的情况下将这些信息通过网络传输,人们经历了各种各样的考验。

更重要的是,媒体验证是字符串编码,因此我们要确保处理应用程序可以接受数据(例如,不包含表示EOL的二进制序列)。

假设你想在一封编码为UTF-8的电子邮件中发送二进制数据——如果1和0的流创建了一个在UTF-8编码中无效的序列,电子邮件可能不会正确显示。

当我们想在URL中编码对URL本身无效的字符时,同样的事情也会发生在URL中:

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

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

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

在你的例子中,man可能是有效的ASCII第一种形式;但通常你可能想要传输随机二进制值(例如在电子邮件中发送图像):

< br > < p > MIME-Version: 1.0 内容描述:"Base64 encode of a.gif"
内容类型:/ gif图像;name = " a.gif " < br > Content-Transfer-Encoding: Base64 < br > 附加:附件;文件名= " a.gif " < / p >

在这里,我们看到GIF图像以base64编码为电子邮件的一个块。电子邮件客户端读取标题并解码。由于编码,我们可以确保GIF不包含任何可能被解释为协议的内容,并且我们避免插入SMTP或POP可能认为重要的数据。

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

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

大多数计算机以8位二进制格式存储数据,但这不是必需的。一些机器和传输介质一次只能处理7位(甚至更少)。这样的介质将以7位的倍数来解释流,因此如果您要发送8位的数据,您将不会在另一端收到您所期望的内容。Base-64只是解决这个问题的一种方法:将输入编码为6位格式,通过媒体发送,然后在接收端解码回8位格式。

我发现方便的一个例子是在尝试在XML中嵌入二进制数据时。SAX解析器错误地解释了一些二进制数据,因为这些数据可以是任何东西,包括XML特殊字符。Base64在发送端对数据进行编码,在接收端对其进行解码,解决了这个问题。

用XML编码二进制数据

假设您想在XML文档中嵌入一对图像。图像是二进制数据,而XML文档是文本。但是XML不能处理嵌入的二进制数据。那么该怎么做呢?

一种选择是用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>

XML解析器将能够正确地解析XML文档并提取图像数据。

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

中使用数据的基本编码 许多情况下存储或传输
环境中的数据,可能用于 遗产的原因,都局限于 US-ASCII[1]数据。基编码可以 也可用于新的应用领域 没有遗产限制, 只是因为它使它成为可能 用文本操作对象 编辑。< / p >

过去,不同的应用 有不同的要求和 因此有时会实现基础 编码略有不同 的方式。今天,协议规范 有时使用基编码 General, and "base64"特别是, 没有精确的描述或者 参考。多用途互联网邮件 扩展(MIME)[4]经常被使用 作为base64 without的引用 考虑到后果 换行或非字母 字符。这样做的目的 规范是建立共同的 字母和编码 考虑。希望这能 减少歧义 文件,通向更好 互操作性。< / p >

Base64最初被设计为一种允许二进制数据附加到电子邮件的方式,作为多用途互联网邮件扩展的一部分。

您的第一个错误是认为ASCII编码和Base64编码是可互换的。事实并非如此。它们有不同的用途。

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

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


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

最初创建了许多不同的编码(例如博多码),每个字符使用不同的位数,直到最终ASCII成为每个字符7位的标准。然而,大多数计算机以字节存储二进制数据,每个字节由8位组成,因此美国信息交换标准代码不适合传输这种类型的数据。有些系统甚至会删除最重要的部分。此外,不同系统之间行尾编码的差异意味着ASCII字符10和13有时也会被修改。

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

为了可靠地发送文本,可以使用您选择的文本编码(例如UTF-8)将第一个编码为字节,然后后来 Base64将结果二进制数据编码为可以安全地以ASCII编码发送的文本字符串。接收方将不得不反转此过程以恢复原始消息。当然,这需要接收方知道使用了哪些编码,而这些信息通常需要单独发送。

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


下面是一个工作示例:

我想发一条短信,两行字:

Hello
world!

如果我把它作为ASCII(或UTF-8)发送,它会像这样:

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

字节10在某些系统中被损坏,因此我们可以将这些字节以Base64编码为Base64字符串:

SGVsbG8Kd29ybGQh

Which when encoded using ASCII looks like this:

83 71 86 115 98 71 56 75 100 50 57 121 98 71 81 104

这里的所有字节都是已知的安全字节,因此任何系统破坏此消息的可能性很小。我可以发送这个而不是我的原始消息,并让接收者反向处理以恢复原始消息。

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

  • 换行符通常以文本模式进行转换。
  • 必须注意不要将NUL字节作为文本字符串的结尾,这在任何带有C血统的程序中都很容易做到。

media that are是什么意思 设计用于处理文本数据”?< / p >

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

他们可以处理二进制=>他们可以

但反过来就不对了。为表示文本而设计的协议可能不恰当地处理恰好包含以下内容的二进制数据:

  • 字节0x0A和0x0D,用于行结束,因平台而异。
  • 其他控制字符,如0x00 (NULL = C字符串结束符),0x03(文本结束),0x04(传输结束),或0x1A (DOS文件结束),可能过早地表示数据结束。
  • 大于0x7F的字节(如果协议是为ASCII设计的)。
  • 无效的UTF-8字节序列。

所以你不能仅仅通过基于文本的协议发送二进制数据。您只能使用表示非空格非控制ASCII字符的字节,其中有94个。选择64进制的原因是,它可以更快地计算2的幂,而64是最大的。

不过有一个问题。这是怎么回事 系统仍然没有达成共识 像这样的编码技术很常见 utf - 8 ?< / p >

至少在网络上,他们大多是这样做的。大多数站点使用UTF-8

在西方的问题是,有很多旧的软件,1字节= 1个字符,不能与UTF-8工作。

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

事实上,微软似乎仍然没有从选择错误的UTF编码中恢复过来。如果您想使用Windows API或Microsoft C运行时库,则只能使用UTF-16或区域设置的“ANSI”编码。这使得使用UTF-8非常痛苦,因为您必须一直进行转换。

而不是转义特殊字符

我将给您一个非常不同但真实的例子:我编写javascript代码以在浏览器中运行。HTML标记有ID值,但是在ID中哪些字符有效是有限制的。

但是我希望我的ID无损地引用文件系统中的文件。现实中的文件中可以有各种各样奇怪而奇妙的字符,从感叹号、重音字符、波浪号,甚至是表情符号!我不能这样做:

<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或其他哈希函数不同,您可以反向编码以找出真正有用的数据。

我希望我64年前就知道Base64。如果使用' encodeURIComponent '和str.replace(‘\n’,’\\n’),我就不会把头发扯掉

SSH传输文本:

如果你试图通过ssh传递复杂的数据(例如,一个dotfile,这样你就可以得到你的shell个性化),在没有base64的情况下,祝你好运。这是你用64进制来做的(我知道你可以使用SCP,但这需要多个命令-这使得ssh到服务器的键绑定复杂化):

  • https://superuser.com/a/1376076/114723 < a href = " https://superuser.com/a/1376076/114723 " > < / >

为什么/如何使用Base64编码?

Base64是一种二进制到文本的编码方案,效率为75%。使用它可以使典型的二进制数据(如图像)可以安全地通过遗留的“非8位干净”通道发送。 在早期的电子邮件网络中(直到20世纪90年代早期),大多数电子邮件消息都是7位US-ASCII字符集的纯文本。因此,许多早期的通信协议标准被设计为在“7位”通信链路上工作,而不是在“8位清洁”上工作。 方案效率是输入的比特数和编码输出的比特数之间的比率。 十六进制(Base16)也是一种二进制到文本的编码方案,效率为50%

Base64编码步骤(简化):

  1. 二进制数据被排列成连续的区块,每个区块24位(3字节)。
  2. 每个24位的数据块被分成4个6位的部分。
  3. 每个6位组被转换为它们对应的Base64字符值,即Base64编码将三个八位字节转换为四个编码字符。输出字节与输入字节之比为4:3(开销33%)。
  4. 有趣的是,相同的字符将根据它们在编码产生四个字符的三个八位组中的位置进行不同的编码。
  5. 接收方将不得不反转此过程以恢复原始消息。

下面是我看了别人贴出来的东西后的理解总结:

重要!

Base64编码并不意味着提供安全性

Base64编码不是用来压缩数据的

为什么使用Base64

Base64是数据的文本表示形式,仅由64个字母数字字符(小写和大写)、+、/和=组成。 这64个字符被认为是“安全的”,也就是说,它们不会被传统计算机和程序误解,不像<\n和许多其他。

什么时候Base64有用

我发现base64在以文本形式传输文件时非常有用。获取文件的字节并将它们编码为base64,传输base64字符串,从接收端执行相反的操作。

这与在电子邮件期间通过SMTP发送附件时使用的过程相同。

如何执行base64编码/解码

从base64文本到字节的转换称为解码。 从字节到base64文本的转换称为编码。这与其他编码/解码的命名方式略有不同

Dotnet和Powershell

微软的Dotnet框架支持将字节编码和解码到base64。在mscorlib库中查找Convert命名空间。

下面是你可以使用的powershell命令:

// Base64 encode PowerShell
// See: https://adsecurity.org/?p=478
$Text='This is my nice cool text'
$Bytes = [System.Text.Encoding]::Unicode.GetBytes($Text)
$EncodedText = [Convert]::ToBase64String($Bytes)
$EncodedText




// Convert from base64 to plain text
[System.Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('VABoAGkAcwAgAGkAcwAgAG0AeQAgAG4AaQBjAGUAIABjAG8AbwBsACAAdABlAHgAdAA='))
Output>This is my nice cool text

Bash有一个内置的base64编码/解码命令。你可以这样使用它:

编码到base64:

echo 'hello' | base64


解码base64编码的文本为普通文本:

echo 'aGVsbG8K' | base64 -d


Node.js也支持base64。下面是一个你可以使用的类:


/**
* Attachment class.
* Converts base64 string to file and file to base64 string
* Converting a Buffer to a string is known as decoding.
* Converting a string to a Buffer is known as encoding.
* See: https://nodejs.org/api/buffer.html
*
* For binary to text, the naming convention is reversed.
* Converting Buffer to string is encoding.
* Converting string to Buffer is decoding.
*
*/
class Attachment {
constructor(){


}


/**
*
* @param {string} base64Str
* @returns {Buffer} file buffer
*/
static base64ToBuffer(base64Str) {
const fileBuffer = Buffer.from(base64Str, 'base64');
// console.log(fileBuffer)
return fileBuffer;
}


/**
*
* @param {Buffer} fileBuffer
* @returns { string } base64 encoded content
*/
static bufferToBase64(fileBuffer) {
const base64Encoded = fileBuffer.toString('base64')
// console.log(base64Encoded)
return base64Encoded
}
}

你会得到这样的文件缓冲区:

  const fileBuffer = fs.readFileSync(path);

或像这样:

const buf = Buffer.from('hey there');

你也可以使用API为你做编码和编码,这里有一个:

要进行编码,需要传入纯文本作为主体。

帖子https://mk34rgwhnf.execute-api.ap-south-1.amazonaws.com/base64-encode

要解码,传入base64字符串作为主体。

帖子https://mk34rgwhnf.execute-api.ap-south-1.amazonaws.com/base64-decode

幻想的例子,当你可能需要base64

这里是一个牵强附会的场景,您可能需要使用base64。

假设你是一名间谍,你的任务是复制并拿回一张有价值的照片,带回你国家的情报机构。

这张照片是在一台不能上网也没有打印机的电脑上拍摄的。你手里只有一支笔和一张纸。没有闪盘,没有CD等等。你会怎么做?

你的第一个选择是把图片转换成二进制的1和0,把这些1和0一个一个地复制到纸上,然后跑过去。

然而,这可能是一个挑战,因为只使用1和0作为字母表来表示一张图片会导致很多1和0。你的纸太小了,你没有时间。另外,1和0越多,出错的几率就越大。

第二个选择是使用十六进制而不是二进制。十六进制允许16个而不是2个可能的字符,所以你有一个更宽的字母表,因此更少的纸张和时间所需。

更好的选择是将图片转换为base64,并利用另一个更大的字符集来表示数据。更少的纸和更少的时间来完成。好了!