Base64編碼規則
網絡信息傳遞時,一些二進制資源往往需要轉換為Base64編碼進行傳輸,以提高傳輸效率,例如webpack提供了將圖片格式文件轉換為Base64格式,下面將簡單介紹Base64編碼的原理和實現過程
Base64編碼原理
Base64是一種基於64個可打印字符來表示二進制數據的表示方法。base64要求將每三個8bits字節轉換為四個6bit的字節(3 * 8 = 4 * 6 = 24),然后將轉換后的6bit往高位添加2個0,組成4個8bit的字節,再根據這4個8bit字節的十進制在索引表中查找對應的值,此時得到的結果就是Base64值。
因此,理論上,轉換后的字符串的長度要比原來的字符串長度長1/3,例如:轉換前的4*6二進制字符串為:aaaaaa bbbbbb cccccc dddddd,在每個字節高位添加兩個零后就是: 00aaaaaa 00bbbbbb 00cccccc 00dddddd,此時長度就比原字符串長度多了1/3。
一下是一段話編碼為Base64前后的變化:
編碼前:
Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.
編碼后:
TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz
IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg
dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu
dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo
ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=
前面提到了Base64算法的索引表,Base64的索引表由64個ASCII字符組成:0-9,26個英文小寫字母a-z,26個英文大寫字母:A-Z,除此之外還有額外兩個字符”+”和”/”。
理論上上面的索引表還要加入padding:”=”,前面提到一個字符串轉成Base64時是生成4個字符,如果待轉換的字符串轉換后不足4個Base64字符,則空白的地方需要使用“=”補充,看下面的例子就能明白:
這里進行一個小結,普通字符串文本轉換為Base64的基本過程為:
字符串對應的字符轉換為ASCII→將該ASCII轉換為8位二進制→將轉換的8位二進制進行6bit拆分,高位填充0,形成新的二進制→根據新的二進制從Base64索引表查找結果
Base64編碼的實現
了解了Base64的轉換規則后,我們通過JavaScript來簡單實現Base的編碼
以及解碼
:

1 var Base64 = { 2 3 _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", 4 5 //將字符串轉換為Base64 6 encode: function(input) { 7 var output = ""; 8 var chr1, chr2, chr3, enc1, enc2, enc3, enc4; 9 var i = 0; 10 11 while (i < input.length) { 12 13 chr1 = input.charCodeAt(i++); 14 chr2 = input.charCodeAt(i++); 15 chr3 = input.charCodeAt(i++); 16 17 enc1 = chr1 >> 2; 18 enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 19 enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 20 enc4 = chr3 & 63; 21 22 if (isNaN(chr2)) { 23 enc3 = enc4 = 64; 24 } else if (isNaN(chr3)) { 25 enc4 = 64; 26 } 27 output = output + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); 28 } 29 return output; 30 }, 31 32 //將Base64解碼為可讀字符串 33 decode: function(input) { 34 var output = ""; 35 var chr1, chr2, chr3; 36 var enc1, enc2, enc3, enc4; 37 var i = 0; 38 39 input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); 40 41 while (i < input.length) { 42 enc1 = this._keyStr.indexOf(input.charAt(i++)); 43 enc2 = this._keyStr.indexOf(input.charAt(i++)); 44 enc3 = this._keyStr.indexOf(input.charAt(i++)); 45 enc4 = this._keyStr.indexOf(input.charAt(i++)); 46 47 chr1 = (enc1 << 2) | (enc2 >> 4); 48 chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); 49 chr3 = ((enc3 & 3) << 6) | enc4; 50 51 output = output + String.fromCharCode(chr1); 52 53 if (enc3 != 64) { 54 output = output + String.fromCharCode(chr2); 55 } 56 if (enc4 != 64) { 57 output = output + String.fromCharCode(chr3); 58 } 59 } 60 return output; 61 } 62 }
首先定義一個Base64的索引值_keyStr
,其中包含了A-/的64個字符,最后還加上填充值’=’,索引值供編碼以及解碼時使用。
首先看編碼方法(encode),定義輸入原始字符串的連續三個字符chr1,chr2,chr3,輸出的4個二進制字符enc1,enc2,enc3,enc4,通過一個變量i
遍歷輸入的原始字符串,以原始字符串“abcd”為例:
第一次循環: 首先將第一次循環的三個字符轉變為ASCII的十進制模式
chr1 = input.charCodeAt(i++);//chr1:a→97 chr2 = input.charCodeAt(i++);//chr2:b→98 chr3 = input.charCodeAt(i++);//chr3:c→99 console.log(chr1)//97(0110 0001) console.log(chr2)//98(0110 0010) console.log(chr3)//99(0110 0011)
有了ASCII的十進制值后,即可使用位運算
,將 3 * 8 的二進制拆分為4 * 6的二進制模式,下面一步一步來看位運算是如何實現4 * 6拆分的:

1 enc1 = chr1 >> 2; 2 /*計算enc1:將chr1右移兩位:0110 0001→0001 1000*/ 3 console.log(enc1)//00011000 4 5 enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 6 /*計算enc2: /*取chr1的最后兩位作為enc2的最高位,取chr2的高四位作為enc2的低四位,將兩者通過或運算結合: /*( chr1 & 3 ) << 4= (0110 0001 & 0000 0011) << 4 = (00000001)<<4 = 0001 0000, /*(chr2 >> 4) = (0110 0010) >> 4 = 00000110, /*enc2=0001 0000 | 0000 0110=0001 0110 /*console.log(enc2)//00010110 */ 7 8 enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 9 /*計算enc3: /*取chr2的低四位和chr3的高兩位組成enc2的低六位(高兩位補零): /*(chr2 & 15) << 2 = (0110 0010 & 0000 1111) << 2 = 0000 0010 << 2 = 0000 1000, /*chr3 >> 6 = 0110 0011 >> 6 = 0000 0001, /*enc3 = 0000 1000 | 0000 0001 = 0000 1001 /*console.log(enc3)//00001001 */ 10 11 enc4 = chr3 & 63; 12 13 /*計算enc4:取chr3余下六位作為低六位(高兩位補零): /*enc4 = 0110 0011 & 0011 1111 = 0010 1011 /*console.log(enc4)//00101011 */ 14
此時通過this._keyStr.charAt(encx)
進行索引查找,得到的前三個字符的編碼為“YWJj”
。
分析完"abcd"
的"abc"
編碼,也就是程序的第一次循環,還剩”d”未解析,此時需要進入第二次循環,由於d之后已經沒有字符,故第二次循環時的chr1
、chr2
、chr3
的結果為:100
,NaN
,NaN
。同理進行3 * 8 = 4 * 6編碼時得到的最后編碼為“ZA==”。
經過兩次循環后,原始字符串”abcd”已經被全部遍歷並編碼,最后得到的Base64編碼為”YWJjZA==”。
理解了encode
方法后,再去進行解碼’decode’就比較簡單了,基本上就是編碼的逆向操作,這里就不一一解釋了。
總結
Base64作為一種傳輸二進制的編碼格式,雖然編碼后字符內容長度會增加大約1/3,但是在一定程度上保證了一些不可打印字符在傳輸時的的信息完整性,同時本文最后也通過使用JavaScript實現了Base64的編碼以及解碼,可以看到Base64也起到了一定的加密作用(起碼不是人一眼就能看懂的),在實際項目中應根據具體需求和環境選用Base64編碼。