在我們項目開發(fā)中,Base64想必大家都不會很陌生,Base64是將「二進制數(shù)據(jù)」轉(zhuǎn)換為文本的一種優(yōu)雅方式,使存儲和傳輸變得容易。但是,作為一個合格的程序員,我們應該有一種打破砂鍋問到底的求助欲望。
所以,今天我們來講講在各種語言中出鏡率都高的離譜的Base64算法。今天,我們就用我們在初高中語文老師教我們的描述一個事物的三大步驟:1. 是什么,2. 如何工作,3. 為什么它很重要。來講講Base64算法。
好了,天不早了,干點正事哇。
「前置知識點」,只是做一個概念的介紹,不會做深度解釋。因為,這些概念在下面文章中會有出現(xiàn),為了讓行文更加的順暢,所以將本該在文內(nèi)的概念解釋放到前面來。「如果大家對這些概念熟悉,可以直接忽略」同時,由于閱讀我文章的群體有很多,所以有些知識點可能「我視之若珍寶,爾視只如草芥,棄之如敝履」。以下知識點,請「酌情使用」。
RFC,全稱為Request for Comments,是一種用于定義「互聯(lián)網(wǎng)標準和協(xié)議」的文件系列。
RFC最早由互聯(lián)網(wǎng)工程任務組(IETF)創(chuàng)建,用于記錄和傳播互聯(lián)網(wǎng)協(xié)議、方法和最佳實踐的提案、規(guī)范和討論。
「每個 RFC 都有一個唯一的編號」,通常以RFC開頭,后面跟著一個數(shù)字,例如RFC 791、RFC 2616等。RFC文檔通常包含了協(xié)議規(guī)范、技術(shù)說明、最佳實踐、標準化提案等,以促進互聯(lián)網(wǎng)技術(shù)的發(fā)展和互操作性。
我們可以在IETF-datatracker[1]中輸入指定的編號或者查找的關(guān)鍵字進行搜尋。
圖片
以下是一些常見的RFC文檔,大家可以翻閱自己想了解的技術(shù)點:
Latin-1,也稱為ISO-8859-1,是一種由國際標準化組織(ISO)認可的「8 位字符集」,代表了「西歐語言的字母表」。正如其名稱所示,「它是ISO-8859的一個子集」,該標準還包括用于寫作系統(tǒng)如西里爾文、希伯來文和阿拉伯文的其他相關(guān)字符集。它被大多數(shù)Unix系統(tǒng)以及Windows系統(tǒng)使用。
Latin-1有時被不太準確地稱為「擴展 ASCII」。
這是因為其字符集的前 128 個字符與美國 ASCII 標準相同。其余字符集包含了帶重音的字符和符號。
關(guān)于更詳細的Latin-1的表格,可以參考Latin-1-table[3]
btoa 是 JavaScript 中的一個內(nèi)置函數(shù),用于將二進制數(shù)據(jù)(通常是 8 位字節(jié))編碼為 Base64 字符串。它的名稱是 binary to ASCII 的縮寫,用于將二進制數(shù)據(jù)轉(zhuǎn)換為文本字符串,以便在文本協(xié)議中傳輸或存儲。
btoa 函數(shù)接受一個字符串參數(shù),該字符串包含二進制數(shù)據(jù)。它將該二進制數(shù)據(jù)轉(zhuǎn)換為 Base64 編碼的字符串。
const binaryData = "front789";const base64String = btoa(binaryData);console.log(base64String);
這段代碼將 front789 這個字符串轉(zhuǎn)換為 Base64 編碼的字符串并將結(jié)果打印到控制臺。
盡管 btoa 是一個有用的函數(shù),但它有一些限制:
Data URL 是一種統(tǒng)一資源標識符(URI)方案,用于將數(shù)據(jù)嵌入到文檔中,而不是從外部文件加載數(shù)據(jù)。Data URL 允許我們將數(shù)據(jù)(如文本、圖像、音頻等)直接包含在網(wǎng)頁或文檔中,而不需要額外的 HTTP 請求。這種方式對于小型資源或需要避免外部請求的情況非常有用。
Data URL 的基本結(jié)構(gòu)如下:
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)進行通信,但人們通常希望使用更豐富的數(shù)據(jù)形式進行通信,如文本或圖像?!笧榱嗽谟嬎銠C之間傳輸數(shù)據(jù),首先必須將其編碼為 0 和 1,然后再解碼」。以文本為例,有許多不同的編碼方式。如果我們都能就一個單一的編碼方式達成一致,那將會簡單得多,但很遺憾,這并不是事實。針對這塊的內(nèi)容,可以參考了不起的 Unicode
最初創(chuàng)建了許多不同的編碼方式(例如 Baudot 編碼),每種方式「使用不同數(shù)量的比特來表示一個字符」,直到最終 ASCII 成為一個標準,「每個字符使用 7 位」。然而,大多數(shù)「計算機將二進制數(shù)據(jù)存儲為每個字節(jié)由 8 位組成的數(shù)據(jù)」,因此 ASCII 不適合傳輸這種類型的數(shù)據(jù)。一些系統(tǒng)甚至會刪除最高位。
為解決這些問題,引入了 Base64 編碼。這允許我們「將任意字節(jié)編碼為已知不會損壞的字節(jié)」(ASCII 字母數(shù)字字符和一些符號)。缺點是使用 Base64 對消息進行編碼會增加其長度 - 「每 3 個字節(jié)的數(shù)據(jù)編碼為 4 個 ASCII 字符」。
要可靠地發(fā)送文本,我們可以首先使用自己選擇的文本編碼(例如 UTF-8)將其編碼為字節(jié),然后將結(jié)果的二進制數(shù)據(jù)使用 Base64 編碼為可安全傳輸?shù)?nbsp;ASCII 文本字符串。接收者反轉(zhuǎn)此過程以恢復原始消息。當然,這需要接收者知道使用了哪種編碼,通常需要單獨發(fā)送這些信息。
我們來看一個示例:
我希望發(fā)送一個帶有兩行的文本消息:
Helloworld!
如果我將其發(fā)送為 ASCII(或 UTF-8),它將如下所示:
72 101 108 108 111 10 119 111 114 108 100 33
某些系統(tǒng)會破壞字節(jié) 10,所以我們可以將這些字節(jié)作為 Base64 字符串進行 Base64 編碼:
SGVsbG8Kd29ybGQh
這里的所有字節(jié)都是已知的安全字節(jié),所以很少有機會使任何系統(tǒng)損壞此消息。我可以發(fā)送這個消息而不是我的原始消息,然后讓接收者反轉(zhuǎn)此過程以恢復原始消息。
Base64編碼將二進制數(shù)據(jù)轉(zhuǎn)換為文本,具體來說是ASCII文本。生成的文本僅包含A-Z、a-z、0-9以及符號+和/這些字符。
而在之前我們在了不起的 Unicode中介紹過ASCII的。
由于字母表中有 26 個字母,我們有26 + 26 + 10 + 2(64)個字符。因此,這種編碼被命名為Base64。這 64 個字符被認為是「安全」的,也就是說,與字符<、>、/n等不同,「它們不會被舊計算機和程序誤解」。
下面是經(jīng)過 Base64 編碼的文本front789的樣子:ZnJvbnQ3ODk=。
還有一點需要注意,如果在使用JS對某一個文本進行準換時,如果該文本包含非Latin1字符的字符串,會報錯,所以我們需要對其進行準換處理。
// 原始文本字符串,包含非Latin1字符const text = "前端柒八九";// 創(chuàng)建一個 TextEncoder 對象,用于將文本編碼為字節(jié)數(shù)組const encoder = new TextEncoder();// 使用 TextEncoder 對象將文本編碼為字節(jié)數(shù)組const data = encoder.encode(text);// 使用 String.fromCharCode 和展開運算符 (...) 將字節(jié)數(shù)組轉(zhuǎn)換為字符串// 然后使用 btoa 函數(shù)將字符串轉(zhuǎn)換為 Base64 編碼const base64 = btoa(String.fromCharCode(...data));// 打印 Base64 編碼后的結(jié)果console.log(base64); //5YmN56uv5p+S5YWr5Lmd
我們在這里并沒有加密文本。給定Base64編碼的數(shù)據(jù),非常容易將其轉(zhuǎn)換回(解碼)原始文本。我們「只是改變了數(shù)據(jù)的表示」,即編碼。
在本質(zhì)上,Base64編碼使用一組特定的、減少的字符來「編碼二進制數(shù)據(jù)」,以防止數(shù)據(jù)損壞。
Base64字母表
由于只有64個字符可用于編碼,我們可以僅使用6位來表示它們,因為2^6 = 64。每個Base64數(shù)字表示6位數(shù)據(jù)。一個字節(jié)中有8位,而 8 和 6 的「最小公倍數(shù)」是 24。因此,「24 位,或 3 個字節(jié),可以用四個 6 位的 Base64 數(shù)字表示」。
我們可能在HTML文檔中使用了<img src="789.jpeg">標簽來包含圖像。其實,我們可以直接將「圖像數(shù)據(jù)」嵌入到 HTML 中,而不必使用外鏈!數(shù)據(jù)URL可以做到這一點,它們使用Base64編碼的文本來內(nèi)聯(lián)嵌入文件。
<img src="data:image/gif;base64,xxxxbase64encodedtextxxxx" />data:[<mime type >][;charset=<charset>][;base64],<encoded data></encoded></charset></mime>
另一個常見的用例是當我們需要在網(wǎng)絡上傳輸或存儲一些二進制數(shù)據(jù),而網(wǎng)絡只能處理文本或ASCII數(shù)據(jù)時。這確保了數(shù)據(jù)在傳輸過程中保持不變。還有就是在 URL 中傳遞數(shù)據(jù)時,當數(shù)據(jù)包含不適合 URL 的字符時,此時Base64就有了用武之地。
Base編碼還在許多應用程序中使用,因為它使得可以使用文本編輯器來操作對象。
我們還可以使用 Base64 編碼「將文件作為文本傳輸」。
以下是將一些文本轉(zhuǎn)換為 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、文本、視頻等),而不是簡單的字符串,我們想將它存儲為文本。我們可以首先將其轉(zhuǎn)換為二進制,然后進行 Base64 編碼,以獲得相應的 ASCII 文本。
現(xiàn)在我們可以將該文本發(fā)送或存儲在任何地方,以任何我們喜歡的方式,而不必擔心一些舊設(shè)備、協(xié)議或軟件會錯誤解釋原始二進制數(shù)據(jù)以損壞我們的文件。
所有編程語言都支持將數(shù)據(jù)編碼為 Base64 格式以及從 Base64 格式解碼數(shù)據(jù)。
// 簡單字符串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 文件中添加以下內(nèi)容:
[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);
想了解更多關(guān)于Rust如何處理Base64,可以查看Rust base64[5]
此外,終端也內(nèi)置支持 Base64 編碼。在終端中嘗試以下命令:
echo "前端柒八九" | base645YmN56uv5p+S5YWr5LmdCg==$ echo "5YmN56uv5p+S5YWr5LmdCg==" | base64 -d前端柒八九
本文鏈接:http://www.www897cc.com/showinfo-26-17389-0.html了不起的Base64
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com