一文講清base64編碼原理
前言
平靜之下,驀然回首,base64 卻在燈火闌珊處。
今天翻開(kāi)舊項(xiàng)目發(fā)現(xiàn)挺多圖片相關(guān)的插件都是用 base64 來(lái)顯示圖片的。談到 base64,腦海遐想翩翩,思緒回蕩之下 base64 瑕瑜互見(jiàn)。本文旨在記錄工作中遇見(jiàn)的問(wèn)題并加以總結(jié),如有不妥請(qǐng)指正~
base64 由來(lái)
base64 是網(wǎng)絡(luò)傳輸 8Bit 字節(jié)代碼的編碼方式之一,是一種基于 64 個(gè)可打印字符來(lái)表示二進(jìn)制數(shù)據(jù)的方法。在做支付系統(tǒng)時(shí),報(bào)文交互都需要使用 base64 對(duì)明文進(jìn)行轉(zhuǎn)碼,然后再進(jìn)行簽名或加密,之后再進(jìn)行(或再次 base64 轉(zhuǎn)碼)傳輸。那么,base64 到底起到什么作用呢?
在參數(shù)傳輸?shù)倪^(guò)程中經(jīng)常遇到的一種情況:使用全英文的字符串沒(méi)問(wèn)題,但一旦涉及到中文就會(huì)出現(xiàn)亂碼的情況。與此類(lèi)似,網(wǎng)絡(luò)上傳輸?shù)淖址⒉蝗强纱蛴〉淖址热缍M(jìn)制文件、圖片等。base64 的出現(xiàn)就是為了解決此問(wèn)題,它是基于 64 個(gè)可打印的字符來(lái)表示二進(jìn)制的數(shù)據(jù)的一種方法。
電子郵件剛問(wèn)世的時(shí)候,只能傳輸英文,但后來(lái)隨著用戶的增加,中文、日韓俄文等文字的用戶也有需求,但這些字符并不能被服務(wù)器或網(wǎng)關(guān)有效處理,因此 base64 就登場(chǎng)了。隨后,base64 在 URL、Cookie、網(wǎng)頁(yè)傳輸少量二進(jìn)制文件中也有相應(yīng)的使用。
base64 的編碼原理
基于a-z
、A-Z
、0-9
、+/
這 64 個(gè)字符來(lái)標(biāo)識(shí)二進(jìn)制數(shù)據(jù),另外=
符號(hào)用于當(dāng)字節(jié)缺位時(shí)補(bǔ)用。
base64 編碼對(duì)照表
base64 編碼對(duì)照表
索引 | 對(duì)應(yīng)字符 | 索引 | 對(duì)應(yīng)字符 | 索引 | 對(duì)應(yīng)字符 | 索引 | 對(duì)應(yīng)字符 |
0 | A | 17 | R | 34 | i | 51 | z |
1 | B | 18 | S | 35 | j | 52 | 0 |
2 | C | 19 | T | 36 | k | 53 | 1 |
3 | D | 20 | U | 37 | l | 54 | 2 |
4 | E | 21 | V | 38 | m | 55 | 3 |
5 | F | 22 | W | 39 | n | 56 | 4 |
6 | G | 23 | X | 40 | o | 57 | 5 |
7 | H | 24 | Y | 41 | p | 58 | 6 |
8 | I | 25 | Z | 42 | q | 59 | 7 |
9 | J | 26 | a | 43 | r | 60 | 8 |
10 | K | 27 | b | 44 | s | 61 | 9 |
11 | L | 28 | c | 45 | t | 62 | + |
12 | M | 29 | d | 46 | u | 63 | / |
13 | N | 30 | e | 47 | v | ||
14 | O | 31 | f | 48 | w | ||
15 | P | 32 | g | 49 | x | ||
16 | Q | 33 | h | 50 | y |
base64 的編碼轉(zhuǎn)換規(guī)則
base64 要求把每三個(gè) 8Bit
的字節(jié)轉(zhuǎn)換
為四個(gè) 6Bit
的字節(jié)(3*8 = 4*6 = 24
),然后把 6Bit再添兩位高位 0
,組成四個(gè) 8Bit 的字節(jié)(4*8=32
)。
為什么使用 3 個(gè)字節(jié)一組呢?因?yàn)?6 和 8 的最小公倍數(shù)
為 24,三個(gè)字節(jié)正好 24 個(gè)二進(jìn)制位,每 6 個(gè) bit 位一組,恰好能夠分為 4 組。
同時(shí)用于分組后每組添加兩個(gè)高位 0,轉(zhuǎn)換后
的字符串理論上
將要比原來(lái)的字符長(zhǎng) 1/3(24/32=1/3)
。
步驟分解:
將待轉(zhuǎn)換的字符串每三個(gè)字符分為一組,每個(gè)字符字節(jié)占 8bit,那么共有 24 個(gè)二進(jìn)制位。
將 24 個(gè)二進(jìn)制位每 6 個(gè)字節(jié)為一組,共分為 4 組。
在每組 6 字節(jié)前面添加兩個(gè) 0,每組由 6 字節(jié)變?yōu)?8 字節(jié)二進(jìn)制位,組成總共 32 個(gè)二進(jìn)制位,即四個(gè)字節(jié)。
根據(jù) base64 編碼對(duì)照表獲得對(duì)應(yīng)的值。
舉個(gè)栗子
- 以標(biāo)準(zhǔn) 3 個(gè)字符
LJY
為例。
LJY
對(duì)應(yīng)的 ASCII 碼值分別為 76、74、89,對(duì)應(yīng)的二進(jìn)制值是 01001100、01001010、01011001。由此組成一個(gè) 24 位的二進(jìn)制位字符串。將 24 位的二進(jìn)制位字符串,按照每 6 位二進(jìn)制位一組分成 4 組。
對(duì) 4 組 每組 6 位二進(jìn)制位字符串前面補(bǔ)兩個(gè) 0,每組擴(kuò)展為 8 位二進(jìn)制位,4 組共擴(kuò)展成 32 個(gè)二進(jìn)制位,此時(shí) 4 組二進(jìn)制位分別為:00010011、00000100、00101001、00011001。其對(duì)應(yīng)的 base64 編碼索引為:19、4、41、25。
用 base64 編碼索引值在 base64 編碼表中進(jìn)行查找,分別對(duì)應(yīng):T、E、p、Z。
- 因此
LJY
base64 編碼之后就變?yōu)椋篢EpZ。
- 以標(biāo)準(zhǔn) 3 個(gè)字符
| 文本 | L | J | Y | | ASCII | 76 | 74 | 89 | | 二進(jìn)制位 | 01001100 | 01001010 | 01011001 | | 分組二進(jìn)制 | 010011 | 000100 | 101001 | 011001 | | 分組二進(jìn)制補(bǔ)2個(gè)0 | 00010011 | 00000100 | 00101001 | 00011001 | | 分組索引 | 19 | 4 | 41 | 25 | | base64編碼 | T | E | p | Z | 主要展示: 轉(zhuǎn)換前二進(jìn)制位: 01001100 01001010 01011001 轉(zhuǎn)換后二進(jìn)制位: 00010011 00000100 00101001 00011001 復(fù)制代碼
- 字符位數(shù)不足情況
上述栗子是面向剛好三個(gè)字符為一組的情況。當(dāng)然不是所有時(shí)候都這么巧字符位數(shù)足夠,除此以外有位數(shù)不足的情況。那么,面對(duì)字符位數(shù)不足的情況下該如何處理呢?
base64 給出的方案是,當(dāng)每組字符不足三位時(shí),不足位數(shù)位置需要使用=
符號(hào)補(bǔ)上。
位數(shù)不足情況處理情景:
- 位數(shù)缺一個(gè)字節(jié):一個(gè)字節(jié)共 8 個(gè)二進(jìn)制位,依舊按照規(guī)則進(jìn)行分組。此時(shí)共 8 個(gè)二進(jìn)制位,每 6 個(gè)一組,則第二組缺少 4 位,用 0 補(bǔ)齊,得到兩個(gè) base64 編碼,而后面兩組沒(méi)有對(duì)應(yīng)數(shù)據(jù),都用
=
補(bǔ)上。 - 位數(shù)缺兩個(gè)字節(jié):兩個(gè)字節(jié)共 16 個(gè)二進(jìn)制位,依舊按照規(guī)則進(jìn)行分組。此時(shí)總共 16 個(gè)二進(jìn)制位,每 6 個(gè)一組,則第三組缺少 2 位,用 0 補(bǔ)齊,得到三個(gè) base64 編碼,第四組完全沒(méi)有數(shù)據(jù)則用
=
補(bǔ)上。
位數(shù)不足圖解如下:
<!-- 缺2位字符,字符串以A為例轉(zhuǎn)換base64后位QQ== --> | 文本(1Byte) | A | | | | 二進(jìn)制位 | 01000001 | | | | 分組二進(jìn)制 | 010000 | 010000 | | | | 分組二進(jìn)制補(bǔ)0 | 00010000 | 00010000 | | | | 分組索引 | 16 | 16 | | | | base64編碼 | Q | Q | = | = | <!-- 缺1位字符,字符串以AB為例轉(zhuǎn)換base64后位QUI= --> | 文本(1Byte) | A | B | | | 二進(jìn)制位 | 01000001 | 01000010 | | | 分組二進(jìn)制 | 010000 | 010100 | 001000 | | | 分組二進(jìn)制補(bǔ)0 | 00010000 | 00010100 | 00001000 | | | 分組索引 | 16 | 20 | 8 | | | base64編碼 | Q | U | I | = | 復(fù)制代碼
列舉了一個(gè)字符到三個(gè)字符轉(zhuǎn)換為 base64 ,可以發(fā)現(xiàn)將 base64 就是按照 base64 編碼對(duì)照表來(lái)將二進(jìn)制轉(zhuǎn)換為字符串,使得數(shù)據(jù)不能直接明文展示出來(lái),但也算不上是加密,而這巧好可用在傳輸、存儲(chǔ)、表示二進(jìn)制領(lǐng)域的情景。
- 另外值得注意的是,不用語(yǔ)言如中文有多種編碼(比如:utf-8、gb2312、gbk 等),不同編碼對(duì)應(yīng) base64 編碼結(jié)果都不一樣。
- 其次在推演過(guò)程中可發(fā)現(xiàn) base64 即用 6 位字節(jié)(2 的 6 次冪就是 64)表示字符同理,Base32 就是用 5 位字節(jié),Base16 就是用 4 位字節(jié)。大家可以按照上面的步驟進(jìn)行演化測(cè)試。
base64 優(yōu)缺點(diǎn)
知道 base64 是什么后,也該到為什么出現(xiàn)了。為什么要是使用 base64 呢,這要從其優(yōu)缺點(diǎn)入手來(lái)選擇適合場(chǎng)景了。
- 優(yōu)勢(shì):
- base64 適合不同平臺(tái)、不同語(yǔ)言的傳輸;
- 頁(yè)面中內(nèi)嵌使用 base64 格式的小圖片,可減少了服務(wù)器訪問(wèn)次數(shù);
- 二進(jìn)制位轉(zhuǎn)換 base64 算法簡(jiǎn)單,對(duì)性能影響不大;
- 缺點(diǎn)
- 二進(jìn)制文件轉(zhuǎn)換為 base64 后,體積大概增加 1/3;
- 在基于 Android6.0 及以下默認(rèn)瀏覽器實(shí)測(cè)場(chǎng)景中發(fā)現(xiàn),某些機(jī)型如中興上傳 base64 圖片會(huì)因?yàn)樽址笮∵^(guò)大導(dǎo)致上傳奔潰的情況。
- 字符長(zhǎng)度過(guò)大的 base64 不適應(yīng)使用在 URL 情景,因?yàn)?IOS 端瀏覽器會(huì)限制 URL 長(zhǎng)度,當(dāng)長(zhǎng)度超過(guò)時(shí)會(huì)自動(dòng)切除多余部分,導(dǎo)致數(shù)據(jù)丟失。
- base64 字符過(guò)大會(huì)導(dǎo)致頁(yè)面加載速度變慢,因此建議 10kb 以下的圖片使用。
- base64 無(wú)法緩存,要緩存只能緩存包含 base64 的文件,比如 js 或者 css;
- 面對(duì)大文件時(shí),會(huì)消耗一定的 CPU 進(jìn)行編解碼
- 二進(jìn)制文件轉(zhuǎn)換為 base64 后,體積大概增加 1/3;
JavaScript 的 base64 轉(zhuǎn)碼方法
Web API 二進(jìn)制與 base64 轉(zhuǎn)換
- atob(encodedData) : 解碼一個(gè) base64 編碼的字符串。
enCodedData,是一個(gè)通過(guò) btoa() 方法編碼的字符串, 為二進(jìn)制字符串包含 base64 編碼的數(shù)據(jù)。并返回包含來(lái)自 encodedData 的解碼數(shù)據(jù)的 ASCII 字符串。
- btoa(stringToEncode) : 創(chuàng)建一個(gè) bas64 編碼的字符串。
stringToEncode 為要編碼的二進(jìn)制字符串。并返回包含 stringToEncode 的 base64 表示形式的 ASCII 字符串。
另外在 JavaScript 中,字符串使用 UTF-16 字符編碼表示:在這種編碼中,字符串表示為 16 位(2 字節(jié))單元的序列。每個(gè) ASCII 字符都可以放入其中一個(gè)單元的第一個(gè)字節(jié),但許多其他字符不能。
base64 在設(shè)計(jì)上需要二進(jìn)制數(shù)據(jù)作為其輸入。就 JavaScript 字符串而言,這意味著每個(gè)字符只占用一個(gè)字節(jié)的字符串。因此,如果將一個(gè)字符串傳遞到 btoa()中,其中包含占用多個(gè)字節(jié)的字符,則會(huì)出現(xiàn)錯(cuò)誤,因?yàn)檫@不被視為二進(jìn)制數(shù)據(jù),因此超 16 位字符在使用 btoa()時(shí)需要先對(duì)字符轉(zhuǎn)碼為二進(jìn)制位。
// 簡(jiǎn)單數(shù)據(jù) const encodedData = btoa('Hello, world'); // encode a string const decodedData = atob(encodedData); // decode the string /* 復(fù)雜數(shù)據(jù) */ // convert a Unicode string to a string in which // each 16-bit unit occupies only one byte function toBinary(string) { const codeUnits = new Uint16Array(string.length); for (let i = 0; i < codeUnits.length; i++) { codeUnits[i] = string.charCodeAt(i); } const charCodes = new Uint8Array(codeUnits.buffer); let result = ''; for (let i = 0; i < charCodes.byteLength; i++) { result += String.fromCharCode(charCodes[i]); } return result; } function fromBinary(binary) { const bytes = new Uint8Array(binary.length); for (let i = 0; i < bytes.length; i++) { bytes[i] = binary.charCodeAt(i); } const charCodes = new Uint16Array(bytes.buffer); let result = ''; for (let i = 0; i < charCodes.length; i++) { result += String.fromCharCode(charCodes[i]); } return result; } // a string that contains characters occupying > 1 byte const myString = '???????'; const converted = toBinary(myString); const encoded = btoa(converted); console.log(encoded); // OCY5JjomOyY8Jj4mPyY= const decoded = atob(encoded); const original = fromBinary(decoded); console.log(original); // ???????
- 兼容性:atob() 方法不支持 IE9 及更早的 IE 版本。
base64 轉(zhuǎn)二進(jìn)制
// base64編碼表 const map = { 0: 52, 1: 53, 2: 54, 3: 55, 4: 56, 5: 57, 6: 58, 7: 59, 8: 60, 9: 61, A: 0, B: 1, C: 2, D: 3, E: 4, F: 5, G: 6, H: 7, I: 8, J: 9, K: 10, L: 11, M: 12, N: 13, O: 14, P: 15, Q: 16, R: 17, S: 18, T: 19, U: 20, V: 21, W: 22, X: 23, Y: 24, Z: 25, a: 26, b: 27, c: 28, d: 29, e: 30, f: 31, g: 32, h: 33, i: 34, j: 35, k: 36, l: 37, m: 38, n: 39, o: 40, p: 41, q: 42, r: 43, s: 44, t: 45, u: 46, v: 47, w: 48, x: 49, y: 50, z: 51, '+': 62, '/': 63, }; function base64to2(base64) { let len = base64.length * 0.75; // 轉(zhuǎn)換為int8array所需長(zhǎng)度 base64 = base64.replace(/=*$/, ''); // 去掉=號(hào)(占位的) const int8 = new Int8Array(len); //設(shè)置int8array視圖 let arr1, arr2, arr3, arr4, p = 0; for (let i = 0; i < base64.length; i += 4) { arr1 = map[base64[i]]; // 每次循環(huán) 都將base644個(gè)字節(jié)轉(zhuǎn)換為3個(gè)int8array直接 arr2 = map[base64[i + 1]]; arr3 = map[base64[i + 2]]; arr4 = map[base64[i + 3]]; // 假設(shè)數(shù)據(jù)arr 數(shù)據(jù) 00101011 00101111 00110011 00110001 int8[p++] = (arr1 << 2) | (arr2 >> 4); // 上面的操作 arr1向左邊移動(dòng)2位 變?yōu)?0101100 // arr2 向右移動(dòng)4位:00000010 // | 為'與'操作: 10101110 int8[p++] = (arr2 << 4) | (arr3 >> 2); int8[p++] = (arr3 << 6) | arr4; } return int8; }
base64 轉(zhuǎn)成 Blob
// base64圖片轉(zhuǎn)blob function base64toBlob(base64) { var arr = base64.split(','), mime = arr[0].match(/:(.*?);/)[1] || 'image/png', bstr = atob(arr[1]), // 將base64轉(zhuǎn)為Unicode規(guī)則編碼 n = bstr.length, u8arr = new Uint8Array(n); while (n--) { u8arr[n] = bstr.charCodeAt(n); // 轉(zhuǎn)換編碼后才可以使用charCodeAt 找到Unicode編碼 } return new Blob([u8arr], { type: mime }); } /* 優(yōu)化版 */ function base64ToBlob(base64) { var arr = base64.split(','); var mime = arr[0].match(/:(.*?);/)[1] || 'image/png'; // 去掉url的頭,并轉(zhuǎn)化為byte var bytes = window.atob(arr[1]); // 處理異常,將ascii碼小于0的轉(zhuǎn)換為大于0 var ab = new ArrayBuffer(bytes.length); // 生成視圖(直接針對(duì)內(nèi)存):8位無(wú)符號(hào)整數(shù),長(zhǎng)度1個(gè)字節(jié) var u8arr = new Uint8Array(ab); for (var i = 0; i < bytes.length; i++) { u8arr[i] = bytes.charCodeAt(i); } return new Blob([u8arr], { type: mime }); }
相關(guān)文獻(xiàn)
到此這篇關(guān)于一文講清base64編碼原理的文章就介紹到這了,更多相關(guān)base64編碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如果你有這33種癥狀了請(qǐng)離開(kāi)電腦去走走吧!
這篇文章主要介紹了如果你有這33種癥狀了請(qǐng)離開(kāi)電腦去走走吧!本文是關(guān)注程序員、上班族身心健康的文章,需要的朋友可以參考下2014-09-09VSCode遠(yuǎn)程連接其他主機(jī)的WSL2的問(wèn)題
這篇文章主要介紹了VSCode遠(yuǎn)程連接其他主機(jī)的WSL2的問(wèn)題,在 Windows 10 上開(kāi)啟 SSH Server 服務(wù),設(shè)置 SSH 連接使用的默認(rèn) Shell,本文給大家介紹的非常詳細(xì),需要的朋友參考下吧2021-07-07使用Windows自帶的IIS服務(wù)搭建本地站點(diǎn)并遠(yuǎn)程訪問(wèn)的操作方法
在Windows系統(tǒng)中實(shí)際上集成了建立網(wǎng)站所必須的軟件環(huán)境,今天就讓我們來(lái)看看,如何使用Windows自帶的網(wǎng)站程序建立網(wǎng)站吧,感興趣的朋友一起看看吧2023-12-12語(yǔ)言編程花絮內(nèi)建構(gòu)建順序示例詳解
這篇文章主要為大家介紹了語(yǔ)言編程花絮內(nèi)建構(gòu)建順序示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03Scala項(xiàng)目構(gòu)建工具sbt和IntelliJ IDEA環(huán)境配置詳解
這篇文章主要介紹了Scala項(xiàng)目構(gòu)建工具sbt和IntelliJ IDEA環(huán)境配置,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10申請(qǐng)Jetbrains系列軟件無(wú)限期免費(fèi)用的方法
這篇文章主要介紹了如何申請(qǐng)Jetbrains系列軟件無(wú)限期免費(fèi)用,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09