ChatGPT解决这个技术问题 Extra ChatGPT

为什么我们使用 Base64?

Wikipedia

当需要对二进制数据进行编码时,通常使用 Base64 编码方案,这些二进制数据需要在旨在处理文本数据的媒体上存储和传输。这是为了确保数据在传输过程中保持完整而不被修改。

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

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

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

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

当我们发送数据时,我们无法确定数据是否会以与我们预期的格式相同的格式进行解释。因此,我们发送以双方都能理解的某种格式(如 Base64)编码的数据。这样,即使发送方和接收方对相同事物的解释不同,但由于它们在编码格式上达成一致,数据也不会被错误地解释。

来自Mark Byers example

如果我想发送

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

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

历史背景:电子邮件服务器曾经是 7 位 ASCII。他们中的许多人会将高位设置为 0,因此您只需发送 7 位值。请参阅en.wikipedia.org/wiki/Email#Content_encoding
您可以(或历史上可以)仅依赖 ascii 的低 7 位在机器之间是相同的 - 或在机器之间可翻译,尤其是当并非所有机器都使用 ascii 时
@Martin,你在开玩笑。 Perl 很难阅读,但 base64 根本不可读。
@Lazer 你的图片不见了
@Lazer,“但字节 10 可能无法正确解释为另一端的换行符。”为什么?双方已就 ASCII 达成一致,他们必须正确解释它!

R
Real Ambush

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

当您以 ASCII 编码文本时,您从文本字符串开始并将其转换为字节序列。

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

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

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

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

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

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

从历史上看,它已被用于对电子邮件中的二进制数据进行编码,其中电子邮件服务器可能会修改行尾。一个更现代的示例是对 embed image data directly in HTML source code 使用 Base64 编码。这里有必要对数据进行编码以避免像'<'这样的字符和'>'被解释为标签。

这是一个工作示例:

我希望发送一条包含两行的短信:

Hello
world!

如果我将其作为 ASCII(或 UTF-8)发送,它将如下所示:

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

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

SGVsbG8Kd29ybGQh

使用 ASCII 编码时,如下所示:

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

这里的所有字节都是已知的安全字节,因此任何系统都不会损坏此消息。我可以发送这个而不是我的原始消息,并让接收者反转该过程以恢复原始消息。


“大多数现代通信协议不会破坏数据” - 尽管例如电子邮件可能会在将消息保存到邮箱时将字符串“\nFrom”替换为“\n>From”。或者 HTTP 标头是换行符终止的,没有可逆的方式来转义数据中的换行符(行继续合并空白),因此您也不能将任意 ASCII 转储到它们中。 base64 比 7 位安全更好,它是字母数字和-=+/ 安全的。
“缺点是使用 Base64 编码消息会增加其长度——每 3 个字节的数据被编码为 4 个字节。”它如何增加到 4 个字节?它不会仍然是 3*8 = 24 位吗?
@Lazer:不。看看你自己的例子 - “Man”是base-64编码为“TWFu”。 3 个字节 -> 4 个字节。这是因为输入可以是 2^8 = 256 个可能的字节中的任何一个,而输出只使用 2^6 = 64 个字节(和 =,以帮助指示数据的长度)。每四重输出 8 位被“浪费”,以防止输出包含任何“令人兴奋”的字符,即使输入包含。
将“在 Base64 中编码数据时,从字节序列开始并将其转换为文本字符串”可能会有所帮助:“在 Base64 中编码数据时,从字节序列开始并将其转换为仅由 ASCII 值组成的字节序列”。仅由 ASCII 字符组成的字节序列是 SMTP 所要求的,这就是为什么使用 Base64(和带引号的可打印)作为内容传输编码的原因。优秀的概述!
我发现一篇后面提到的帖子谈论这个“如果我们不这样做,那么某些字符可能会被错误解释。例如,换行符,如 0x0A 和 0x0D,控制字符,如 ^C,^D,和 ^Z 在某些平台上被解释为文件结尾,NULL 字节作为文本字符串的结尾,高于 0x7F 的字节(非 ASCII),我们在 HTML/XML 文档中使用 Base64 编码来避免像 '< ' 和 '>' 被解释为标签。”
S
Sridhar Sarnobat

在 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 文档并提取图像数据。


这可能是 Microsoft 旧的 .mht 格式的工作方式(html 文件 + 单个文件中的图像)。
C
Community

为什么不看the RFC that currently defines Base64

数据的基本编码在许多情况下用于在可能由于遗留原因仅限于 US-ASCII [1] 数据的环境中存储或传输数据。基本编码也可用于没有遗留限制的新应用程序,仅仅是因为它可以使用文本编辑器来操作对象。过去,不同的应用程序有不同的要求,因此有时以稍微不同的方式实现基本编码。今天,协议规范有时通常使用基本编码,特别是“base64”,没有精确的描述或参考。多用途 Internet 邮件扩展 (MIME) [4] 通常用作 base64 的参考,而不考虑换行或非字母字符的后果。本规范的目的是建立通用字母表和编码注意事项。这有望减少其他文档中的歧义,从而实现更好的互操作性。

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


这是公平的,但引出了一个问题,当我们不限于 US-ASCII 时,为什么我们今天仍然使用它
H
Håvard S

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

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


所以就像 Base64 一样,大多数情况下,源和目标都会以相同的方式解释数据,因为它们很可能会以相同的方式解释这 64 个字符,即使它们以不同的方式解释控制字符。那正确吗?
他们的数据甚至可能在传输过程中被破坏。例如,如果服务器和客户端的操作系统不匹配并且传输被标记为文本模式,许多 FTP 程序会将行尾从 13,10 重写为 10 或反之亦然。 FTP 只是我想到的第一个例子,它不是一个好例子,因为 FTP 确实支持二进制模式。
@nhnb:我认为 FTP 是一个很好的例子,因为它表明文本模式不适合需要二进制数据的东西。
什么是文字媒体?
但这引出了其他协议使用什么的问题,如果不是 base64。不是每个协议都有需要保留某些字节作为控制字符的问题吗?然而我只看到 base64 被用于电子邮件和表单数据。
A
Aiden Bell

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

想象一下,您想在电子邮件中发送二进制数据,编码为 UTF-8——如果 1 和 0 的流创建的序列在 UTF-8 编码中不是有效的 Unicode,则电子邮件可能无法正确显示。

当我们想要对 URL 本身中的 URL 无效的字符进行编码时,URL 中也会发生相同类型的事情:

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

这是因为我们想通过一个认为该空间有异味的系统发送一个空间。

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

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

MIME 版本:1.0 内容描述:“a.gif 的 Base64 编码”内容类型:image/gif; name="a.gif" 内容传输编码:Base64 内容处置:附件;文件名="a.gif"

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


太棒了——这个解释让它点击了。这不是为了混淆或压缩数据,而只是为了避免使用可以被解释为协议的特殊序列。
S
Sridhar Sarnobat

Base64 而不是转义特殊字符

我会给你一个非常不同但真实的例子:我编写 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 或其他哈希函数不同,您可以反转编码以找出真正有用的数据。

我希望我在几年前就知道 Base64。我本来可以避免用“encodeURIComponent”和str.replace(‘\n’,’\\n’)撕掉我的头发

SSH 传输文本:

如果你试图通过 ssh 传递复杂的数据(例如一个点文件,这样你就可以获得你的 shell 个性化),祝你好运,不用 Base 64。这就是你使用 base 64 的方式(我知道你可以使用 SCP,但这需要多个命令 - 这会使 ssh 到服务器的键绑定复杂化):

https://superuser.com/a/1376076/114723


G
Gilbert

以下是我在阅读其他人发布的内容后的理解总结:

重要的!

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

Base64 编码并不意味着压缩数据

为什么我们使用 Base64

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

Base64 什么时候有用

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

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

如何执行base64编码/解码

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

dotnet 和 Powershell

Microsoft 的 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


为什么我们不使用基于 256 来减小消息的大小?我特别好奇为什么 ppl 在图像上使用 base64,因为它会增加文件大小
我们不使用base64,因为我们想节省字节
节省字节是什么意思
@StanPeng 我是说我们不在乎文本有多大。 Base64 引入了一些开销,但它解决了问题。例如,想象一下使用肥皂之类的协议发送图像。您可以旧的发送文本、blob 或文件是不允许的。你做什么工作?您将图像转换为 base64 文本并发送它。
如果我们发送一个缓冲区 int 数组,它可以成功。我已经在休息时完成了它,但数组可能已序列化为文本。
C
Community

我发现它很方便的一个例子是尝试 embed binary data in XML 时。 SAX 解析器误解了一些二进制数据,因为这些数据实际上可以是任何东西,包括 XML 特殊字符。 Base64 在发送端对数据进行编码并在接收端对其进行解码,从而解决了该问题。


+1 -- 但这绝不是 SAX 特有的。任何 XML 解析器都会发生这种情况,即 DOM 或 XLINQ。
@比利:是的,绝对。我只是碰巧为该应用程序使用了 SAX 解析器。
不同的引擎,例如 SAX 解析器可能会以不同的方式(不同的控制字符)解释一些 ASCII 值。因此,这里的想法是使用具有普遍意义的 ASCII 子集。正确的?
@Lazer:对。当您尝试将其解释为 ASCII 时(在这种情况下不是),未编码的二进制数据中会偶然包含控制字符。
c
casablanca

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


如果流在 7 位后中断,为什么会出现问题。最后,对方机器将所有数据通过流接收,那么它可以选择8位格式显示吗?我脑子怎么了!
@mallaudin你是对的,base64的真正原因是通过避免在解释该文本的解析应用程序的文本层中使用特殊字符来防止误解(这是在应用程序层而不是物理层中使用的东西)
j
jamesdlin

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

换行符通常以文本模式转换。

必须注意不要将 NUL 字节视为文本字符串的结尾,这在任何具有 C 血统的程序中都很容易做到。


还有一些控制字符,如 ^C、^D 和 ^Z,在某些平台上被解释为文件结尾。
d
dan04

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

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

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

但反之则不然。旨在表示文本的协议可能会不正确地处理碰巧包含以下内容的二进制数据:

字节 0x0A 和 0x0D,用于行尾,因平台而异。

其他控制字符,例如 0x00(NULL = C 字符串终止符)、0x03(END OF TEXT)、0x04(END OF TRANSMISSION)或 0x1A(DOS end-of-file),它们可能会过早地发出数据结束的信号。

高于 0x7F 的字节(如果是为 ASCII 设计的协议)。

UTF-8 无效的字节序列。

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

一个问题。那些系统为什么仍然不同意像如此常见的 UTF-8 这样的常见编码技术?

至少在 Web 上,他们大部分都有。 A majority of sites use UTF-8

西方的问题是,有很多旧软件都认为 1 字节 = 1 字符并且不能使用 UTF-8。

东方的问题是他们对 GB2312 和 Shift_JIS 等编码的依恋。

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


M
Mushtaq Hussain

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

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

Base64 编码步骤(简化):

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


d
dirkgently

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

回到 ASCII 统治世界的那一天,处理非 ASCII 值是一件令人头疼的事情。人们跳过了各种各样的圈子,以便在不丢失信息的情况下通过网络传输这些信息。


实际上,在过去,ASCII 甚至没有在任何地方使用。许多协议都有单独的文本模式和二进制模式来传输数据,不幸的是当时电子邮件没有。文本模式是必要的,因为没有单一的文本编码统治世界,而不是 ASCII;每个计算机网络都有自己喜欢的编码,因此有网关的工作是将交换的文本转换为本地编码,这样日本公司就可以在没有 mojibake 的情况下向美国商业顾问发送电子邮件。显然,这种转换在发送二进制数据时是不可取的。