深入理解Base64編碼原理
前言
上篇文章有涉及到Base64編碼的內容,今天我們再來詳細了解一下Base64的編碼原理以及應用場景。
通過這篇文章你能夠學習到:
- 什么是Base64,為什么需要Base64?
- Base64的編碼原理
- Base64的應用場景
什么是Base64?
Base64是一種用于傳輸8bit字節數據的編碼方式,Base64 的字符集包含 64 個字符(A-Z、a-z、0-9、+、/)以及補位的=
??需要注意的是它只是一種編碼方式,并不是加密方式?。?!因為對于Base64來講,它沒有密鑰的概念,這意味著任何人都能輕松地將Base64編碼的數據還原為原始字符。
盡管如此,不少人仍誤將其當作加密工具來使用,這在具備基本技術知識的人眼中,無異于未加密處理...
為什么需要Base64編碼?
Base64編碼最初主要應用于郵件傳輸協議中,由于這些協議僅支持ASCII字符的傳遞,導致直接傳輸二進制文件(如圖片、視頻等)成為不可能。為了解決這一問題,Base64被設計出來,它能夠將二進制文件內容轉換成僅包含ASCII字符的編碼形式,從而實現在郵件傳輸協議中安全、有效地傳遞二進制數據。
編碼原理
Base64 編碼的核心原理是將輸入數據(多為二進制形式)轉換成特定字符序列。具體步驟為:首先將輸入數據分割成每三個字節(共24位)一組,接著將這24位分割為四個6位的塊(因為Base64中每個字符代表6位二進制數據)。最后,通過查找表將這些6位塊映射為相應的Base64字符。
base64字符集
上面我們提到標準的Base64一般包含64個字符再加一個補位的=
- 大寫字母:A-Z(26 個字符)
- 小寫字母:a-z(26 個字符)
- 數字:0-9(10 個字符)
- 特殊字符:+ 和 /(2 個字符)
- 補位字符:=
編碼步驟
- 分組:將輸入數據按每三個字節一組進行劃分,每組組成一個24位的二進制數據塊。
- 分割:將每個24位的數據塊進一步分割成四個6位的數據塊。
- 字符映射:通過查找字符集,將每個6位數據塊映射為字符集中的對應字符。
- 填充處理:若輸入數據的字節數非3的倍數,則在數據末尾添加=字符作為填充,以確保編碼結果的長度符合Base64規范。
怎么理解這些步驟?
以南玖的南拼音為例
首先將字符對應的二進制位表示出來
圖片
剛好nan是3個字節,它們的二進制位正好組成了一個24位的二進制塊
接著把這個二進制塊分割成4個6位的數據塊
圖片
最后通過查找Base64編碼對照表,找到每個6位數據塊對應的字符
圖片
最終nan編碼為bmFu
由于nan的字節數正好是3的倍數,所以它不需要補位,編碼后也就不會出現=
補位
如果字節數不是3的倍數,那么余數可能是1或2,所以補位也需要分兩種情況。
- 余數為1,二進制末尾補4個0,最后多出來的這個字符會編碼成2個base64字符,最后再補兩個=
比如宋的拼音song,余數為1
圖片
在這基礎上最后還得補上2個=,最終song編碼為c29uZw==
- 余數為2,二進制末尾補2個0,編碼后末尾再補1個=
比如ab,余數為2
圖片
最終ab編碼為YWI=
驗證
在javaScript中可以調用btoa來進行base64編碼
圖片
動手實現一下Base64編解碼
base64編碼
// 自定義base64編碼
const customEncrypt = (str: string) => {
// base64字符集
const base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
// 將字符串轉中的每個字符轉換成8位二進制
const binaryStr = str.split('').map(char => {
const binary = char.charCodeAt(0).toString(2)
return binary.padStart(8, '0')
}).join('')
// 將二進制字符串按6位分割
const binaryArr = binaryStr.match(/.{1,6}/g) || []
// 如果最后一組不是6位的倍數,后面補0
const last = binaryArr[binaryArr.length - 1]
if(last?.length % 6 !== 0) {
binaryArr[binaryArr.length - 1] = last.padEnd(6, '0')
}
// 將6位的二進制轉換成10進制
const decimalArr = binaryArr.map(binary =>parseInt(binary, 2))
// 根據10進制的值獲取base64字符
let base64Str = decimalArr.map(decimal => base64Chars[decimal]).join('')
// 補位
while(base64Str.length % 4 !== 0) {
base64Str += '='
}
return base64Str
}
基本按照上面的編碼步驟實現即可
驗證
console.log('btoa', btoa('song'))
console.log('自定義加密', customEncrypt('song'))
圖片
base64解碼
解碼的過程基本就是與編碼反過來
// 自定義base64解碼
const customDecrypt = (str: string) => {
// base64字符集
const base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
// 將base64字符轉換成10進制
const decimalArr = str.split('').map(char => base64Chars.indexOf(char))
// 將10進制轉換成6位二進制
const binaryArr = decimalArr.map(decimal => decimal.toString(2).padStart(6, '0'))
// 將6位的二進制拼接
const binaryStr = binaryArr.join('')
// 將8位的二進制轉換成字符
const charArr = binaryStr.match(/.{1,8}/g) || []
return charArr.map(binary =>String.fromCharCode(parseInt(binary, 2))).join('')
}
驗證
console.log('atob', atob('c29uZw=='))
console.log('自定義解碼', customDecrypt('c29uZw=='))
圖片
思考??
按照這個思路我們是不是可以實現一個比Base64更安全的偽加密方法
比如:
- 更換字符集
- 更換二進制分割手段
應用場景
數據傳輸
Base64編碼是一種在HTTP文本協議中傳輸二進制數據的常用方法。由于HTTP協議本質上是基于文本的,它限制了只能傳輸可打印的ASCII字符(范圍從32到126),這包括字母、數字、標點符號和一些特殊符號。然而,二進制數據包含許多不在這個范圍內的字符,因此無法直接通過HTTP協議進行傳輸。Base64編碼不僅解決了在HTTP協議中傳輸二進制數據的問題,還確保了數據的完整性和可讀性。
數據存儲
Base64 編碼常用于存儲二進制數據,如數據庫中的圖像、文件等,因為它將數據轉換為可打印字符,避免了二進制數據在存儲過程中可能出現的問題。
在前端頁面實現中,為了提高加載效率,簡單圖片通常會選擇直接內嵌而非加載外部資源。然而,圖片是二進制數據,直接嵌入并不簡單。幸運的是,現代瀏覽器普遍支持Data URLs功能,該功能通過Base64編碼將圖片或其他文件的二進制數據轉換為文本字符串,從而可以方便地嵌入到網頁中。這樣,就無需進行額外的外部資源加載,有助于減少頁面加載時間。
協議編碼
Base64編碼最初主要應用于郵件傳輸協議中,由于這些協議僅支持ASCII字符的傳遞,導致直接傳輸二進制文件(如圖片、視頻等)成為不可能。為了解決這一問題,Base64被設計出來,它能夠將二進制文件內容轉換成僅包含ASCII字符的編碼形式,從而實現在郵件傳輸協議中安全、有效地傳遞二進制數據。
總結
Base64編碼是一種廣泛應用的編碼方法,它將二進制數據轉換為可打印的ASCII字符集,特別適用于數據傳輸和存儲場景。然而,重要的是要認識到,Base64編碼本身并不具備數據加密或安全保護的功能。在需要處理敏感信息時,僅憑Base64編碼是遠遠不夠的,必須結合適當的加密技術和安全傳輸協議(如HTTPS)來確保信息的安全性和隱私性。