@TOC
常見(jiàn)對(duì)base64的認(rèn)知(不完全正確)
首先對(duì)base64常見(jiàn)的認(rèn)知,也是須知的必須有以下幾點(diǎn)*
- base64是一種圖片編碼方式,用一長(zhǎng)串超長(zhǎng)的字符串表示圖片
- 在加載的時(shí)候會(huì)直接以字符串的形式加載出來(lái),減少了圖片加載的http請(qǐng)求
- 正常加載服務(wù)器靜態(tài)資源的時(shí)候都應(yīng)該是通過(guò)http請(qǐng)求回來(lái),每加載一張圖片時(shí)需要發(fā)起一次http請(qǐng)求 ,http請(qǐng)求建立需要一定的時(shí)間,所以對(duì)于小圖而且出現(xiàn)頻次比較高的話,這樣的成本消耗其實(shí)是特別浪費(fèi)的
- 所以一般base64編碼適用于小圖片,出現(xiàn)頻次比較高的情況
當(dāng)然base64編碼也有一定的缺點(diǎn)
- 會(huì)增加圖片本上的大小,對(duì)于小圖來(lái)說(shuō),轉(zhuǎn)碼增加的大小和http請(qǐng)求發(fā)起的浪費(fèi)時(shí)間相比還是劃算的,但是對(duì)于大圖和出現(xiàn)次數(shù)比較少的情況,這種方法就有待商榷
- 當(dāng)然上面我現(xiàn)在項(xiàng)目這種問(wèn)題就很不合適,肯定需要尋求一個(gè)好的方式來(lái)解決掉這個(gè)問(wèn)題
多問(wèn)一個(gè)為什么,base64到底是個(gè)啥?
- base64是一種編碼方式,將二進(jìn)制編碼為64字符串組成的字符碼
- 標(biāo)準(zhǔn)的Base64并不適合直接放在URL里傳輸,因?yàn)閁RL編碼器會(huì)把標(biāo)準(zhǔn)Base64中的“/”和“+”字符變?yōu)樾稳纭?XX”的形式,而這些“%”號(hào)在存入數(shù)據(jù)庫(kù)時(shí)還需要再進(jìn)行轉(zhuǎn)換,因?yàn)锳NSI SQL中已將“%”號(hào)用作通配符。
- 為解決此問(wèn)題,可采用一種用于URL的改進(jìn)Base64編碼,它在末尾填充'='號(hào),并將標(biāo)準(zhǔn)Base64中的“+”和“/”分別改成了“-”和“_”,這樣就免去了在URL編解碼和數(shù)據(jù)庫(kù)存儲(chǔ)時(shí)所要作的轉(zhuǎn)換,避免了編碼信息長(zhǎng)度在此過(guò)程中的增加,并統(tǒng)一了數(shù)據(jù)庫(kù)、表單等處對(duì)象標(biāo)識(shí)符的格式。
- 另有一種用于正則表達(dá)式的改進(jìn)Base64變種,它將“+”和“/”改成了“!”和“-”,因?yàn)椤?”,“*”以及前面在IRCu中用到的“[”和“]”在正則表達(dá)式中都可能具有特殊含義。
- 此外還有一些變種,它們將“+/”改為“-”或“.”(用作編程語(yǔ)言中的標(biāo)識(shí)符名稱)或“.-”(用于XML中的Nmtoken)甚至“_:”(用于XML中的Name)。
- Base64要求把每三個(gè)8Bit的字節(jié)轉(zhuǎn)換為四個(gè)6Bit的字節(jié)(38 = 46 = 24),然后把6Bit再添兩位高位0,組成四個(gè)8Bit的字節(jié),也就是說(shuō),轉(zhuǎn)換后的字符串理論上將要比原來(lái)的長(zhǎng)1/3。
ok,我承認(rèn)以上都是百度出來(lái)了,接下來(lái)談?wù)勎易约旱恼J(rèn)識(shí),哈哈
直接掏個(gè)例子吧,比如,原生js是自帶base64的編碼方法的
var b = Buffer.from('asdasds'); //buffer 是js里面專門存放二進(jìn)制的緩存區(qū),暫時(shí)理解創(chuàng)建一個(gè)二進(jìn)制變量
var s = b.toString('base64');
console.log(s)
// YXNkYXNkcw==
按照我們的思路實(shí)現(xiàn)一下
- base64是針對(duì)二進(jìn)制對(duì)象進(jìn)行編碼,所以我們要將字符轉(zhuǎn)換為二進(jìn)制碼
- base64 是用64個(gè)字符表示二進(jìn)制,2的6次方 = 64,所以base64的字符其實(shí)是每6個(gè)二進(jìn)制位為單位,但是一個(gè)字節(jié)是8bit,如果不滿6的倍數(shù)要往 字節(jié)轉(zhuǎn)換后的二進(jìn)制編碼后面補(bǔ)0,比如如果是兩個(gè)個(gè)字符
'ac' =》 轉(zhuǎn)換為二進(jìn)制為:'0110 0001 0110 0010' =》
如果要將這兩個(gè)字符進(jìn)行base64編碼,但是base64僅支持6位二進(jìn)制轉(zhuǎn)換為一個(gè)字符,
截取之后就是=》 011000 010110 0010
那最后面的4位二進(jìn)制不夠轉(zhuǎn)碼,所以會(huì)在后面默認(rèn)補(bǔ)零 - 補(bǔ)碼完成之后開(kāi)始轉(zhuǎn)碼 從000000 到111111分別對(duì)應(yīng)
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=
64個(gè)字符中的一個(gè) - 轉(zhuǎn)碼完成
轉(zhuǎn)換字符為二進(jìn)制數(shù)
function toBinary (str){
let tempResult = [];
let result = [];
// 分割字符
str.split('').forEach(element => {
//轉(zhuǎn)二進(jìn)制
let binaryElement = element.charCodeAt().toString(2)
//由于js原生方法轉(zhuǎn)二進(jìn)制如果前面是0可能會(huì)不滿8位,所以前面補(bǔ)0,轉(zhuǎn)為8位的對(duì)應(yīng)ascii碼二進(jìn)制
binaryElement = binaryElement.length === 8 ? binaryElement : ('0' + binaryElement) //不足8位的二進(jìn)制碼在前面補(bǔ)0
tempResult.push(binaryElement);
});
let index = 0;
// 不滿3個(gè)字符往后面補(bǔ)滿3個(gè)字符(3個(gè)字符(24個(gè)二進(jìn)制位)是6和8的最小公倍數(shù))
while(tempResult.length % 3 != 0){
tempResult.push('00000000')
}
console.log(tempResult.length)
return tempResult.join('');
}
let binary = toBinary('asdasds');
那么就是第一步和第二步實(shí)現(xiàn)了
二進(jìn)制轉(zhuǎn) base64字符串
//將字符串存為數(shù)組
let KEYCODE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".split('');
function toBase64 (binary){
console.log(binary);
let tempResult = [];
let result = [];
let index = 0;
// 每6位切割二進(jìn)制
while(index+6 < binary.length){
tempResult.push(binary.slice(index,index+6))
index = index + 6 ;
}
//不滿6位的前面補(bǔ)0
console.log(binary.slice(index,index+6))
tempResult.push(("000000" + binary.slice(index,index+6)).substr( -6 ));
tempResult.forEach(element => {
//將二進(jìn)制轉(zhuǎn)為數(shù)組下標(biāo)
let index = parseInt(element,2);
//獲取對(duì)應(yīng)下標(biāo)字符串
result.push(index === 0 ? '=' : KEYCODE[index])
});
//字符串拼接
return result.join('')
}
let a = toBase64(binary);
console.log(a);
// YXNkYXNkcw==
到這里基本就實(shí)現(xiàn)了,結(jié)果跟原生的方法打印的是一樣的
但是也存在一些問(wèn)題和改進(jìn)
-
對(duì)于中文字符和特殊字符的支持
javascript中的中文都是默認(rèn)utf-16編碼,但是網(wǎng)頁(yè)中編碼格式基本都是UTF-8,然而即便我們用UTF-8格式保存了HTML文件,但是其中的中文字符依然是以UTF-16的形式保存的。所以我們首先要將中文字符轉(zhuǎn)化為utf-8,然后再轉(zhuǎn)二進(jìn)制,最后即可用上面的方法進(jìn)行編碼
代碼如下:var utf16ToUtf8 = function (utf16Str) { var utf8Arr = []; var byteSize = 0; var tempList = []; for (var i = 0; i < utf16Str.length; i++) { //獲取字符Unicode碼值 var code = utf16Str.charCodeAt(i); //如果碼值是1個(gè)字節(jié)的范圍,則直接寫入 if (code >= 0x00 && code <= 0x7f) { byteSize += 1; utf8Arr.push(code); //如果碼值是2個(gè)字節(jié)以上的范圍,則按規(guī)則進(jìn)行填充補(bǔ)碼轉(zhuǎn)換 } else if (code >= 0x80 && code <= 0x7ff) { byteSize += 2; utf8Arr.push((192 | (31 & (code >> 6)))); utf8Arr.push((128 | (63 & code))) } else if ((code >= 0x800 && code <= 0xd7ff) || (code >= 0xe000 && code <= 0xffff)) { byteSize += 3; utf8Arr.push((224 | (15 & (code >> 12)))); utf8Arr.push((128 | (63 & (code >> 6)))); utf8Arr.push((128 | (63 & code))) } else if(code >= 0x10000 && code <= 0x10ffff ){ byteSize += 4; utf8Arr.push((240 | (7 & (code >> 18)))); utf8Arr.push((128 | (63 & (code >> 12)))); utf8Arr.push((128 | (63 & (code >> 6)))); utf8Arr.push((128 | (63 & code))) } } var toBin = (n) => { if(n == 0) return '0'; var res = ''; while(n != 0) { res = n % 2 + res n = parseInt(n / 2) } return res; } utf8Arr.forEach(element => { tempList.push(toBin(element)) }); return tempList.join('') }
-
如何對(duì)圖片base64編碼進(jìn)行實(shí)現(xiàn)
圖片的話,要用到canvas ,將圖片轉(zhuǎn)換為二進(jìn)制流,然后再掉用上述的編碼方法
下一次
- 可以嘗試圖片的base64編碼
- 可以做解碼過(guò)程