原生js實(shí)現(xiàn)base64編碼方法

@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ò)程
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 每個(gè)文本編輯器都有默認(rèn)的編碼方式(比如 UTF-8 編碼),當(dāng)我們保存文檔的時(shí)候,可以選擇編碼方式,如果沒(méi)有特意選...
    _于曼麗_閱讀 1,560評(píng)論 0 1
  • 在軟件的編碼和實(shí)現(xiàn)中,我們可能會(huì)碰到個(gè)一個(gè)比較頭疼的問(wèn)題--編碼,不同字符間的編碼和解碼,你確定了解各種字符的編碼...
    Java小鋪閱讀 2,542評(píng)論 0 5
  • Base64是一種基于64個(gè)可打印字符來(lái)表示二進(jìn)制數(shù)據(jù)的表示方法。它已經(jīng)成為網(wǎng)絡(luò)上常見(jiàn)的傳輸8Bit字節(jié)代碼的編碼...
    mysimplebook閱讀 3,171評(píng)論 0 3
  • 原文在這里:各種字符集和編碼詳解 在軟件的編碼和實(shí)現(xiàn)中,我們可能會(huì)碰到個(gè) 一個(gè)比較頭疼的問(wèn)題--編碼,不同字符間的...
    舌尖上的大胖閱讀 1,832評(píng)論 0 2
  • 守不住寂寞的人,也守不住繁華。 一位老先生曾經(jīng)指點(diǎn)他的弟子: “你只需要靜下心來(lái)去鉆研學(xué)問(wèn),有無(wú)教授頭銜又有什么關(guān)...
    悟理趣道閱讀 336評(píng)論 2 14