原生javascript實現base64編碼

@



常見對base64的認知(不完全正確)

首先對base64常見的認知,也是須知的必須有以下幾點*

  • base64是一種圖片編碼方式,用一長串超長的字符串表示圖片
  • 在加載的時候會直接以字符串的形式加載出來,減少了圖片加載的http請求
  • 正常加載服務器靜態資源的時候都應該是通過http請求回來,每加載一張圖片時需要發起一次http請求 ,http請求建立需要一定的時間,所以對於小圖而且出現頻次比較高的話,這樣的成本消耗其實是特別浪費的
  • 所以一般base64編碼適用於小圖片,出現頻次比較高的情況

當然base64編碼也有一定的缺點

  • 會增加圖片本上的大小,對於小圖來說,轉碼增加的大小和http請求發起的浪費時間相比還是划算的,但是對於大圖和出現次數比較少的情況,這種方法就有待商榷
  • 當然上面我現在項目這種問題就很不合適,肯定需要尋求一個好的方式來解決掉這個問題

多問一個爲什麼,base64到底是個啥?

  • base64是一種編碼方式,將二進制編碼爲64字符串組成的字符碼
  • 標準的Base64並不適合直接放在URL裏傳輸,因爲URL編碼器會把標準Base64中的“/”和“+”字符變爲形如“%XX”的形式,而這些“%”號在存入數據庫時還需要再進行轉換,因爲ANSI SQL中已將“%”號用作通配符。
  • 爲解決此問題,可採用一種用於URL的改進Base64編碼,它在末尾填充'='號,並將標準Base64中的“+”和“/”分別改成了“-”和“_”,這樣就免去了在URL編解碼和數據庫存儲時所要作的轉換,避免了編碼信息長度在此過程中的增加,並統一了數據庫、表單等處對象標識符的格式。
  • 另有一種用於正則表達式的改進Base64變種,它將“+”和“/”改成了“!”和“-”,因爲“+”,“*”以及前面在IRCu中用到的“[”和“]”在正則表達式中都可能具有特殊含義。
  • 此外還有一些變種,它們將“+/”改爲“-”或“.”(用作編程語言中的標識符名稱)或“.-”(用於XML中的Nmtoken)甚至“_:”(用於XML中的Name)。
  • Base64要求把每三個8Bit的字節轉換爲四個6Bit的字節(38 = 46 = 24),然後把6Bit再添兩位高位0,組成四個8Bit的字節,也就是說,轉換後的字符串理論上將要比原來的長1/3。

ok,我承認以上都是百度出來了,接下來談談我自己的認識,哈哈

直接掏個例子吧,比如,原生js是自帶base64的編碼方法的

var b = Buffer.from('asdasds'); //buffer 是js裏面專門存放二進制的緩存區,暫時理解創建一個二進制變量
var s = b.toString('base64');
console.log(s)
//  YXNkYXNkcw==

按照我們的思路實現一下

  • base64是針對二進制對象進行編碼,所以我們要將字符轉換爲二進制碼
  • base64 是用64個字符表示二進制,2的6次方 = 64,所以base64的字符其實是每6個二進制位爲單位,但是一個字節是8bit,如果不滿6的倍數要往 字節轉換後的二進制編碼後面補0,比如如果是兩個個字符
    'ac' =》 轉換爲二進制爲:'0110 0001 0110 0010' =》
    如果要將這兩個字符進行base64編碼,但是base64僅支持6位二進制轉換爲一個字符,
    截取之後就是=》 011000 010110 0010
    那最後面的4位二進制不夠轉碼,所以會在後面默認補零
  • 補碼完成之後開始轉碼 從000000 到111111分別對應ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=64個字符中的一個
  • 轉碼完成

轉換字符爲二進制數

function toBinary (str){
    let tempResult = [];
    let result = [];
    // 分割字符
    str.split('').forEach(element => {
    	//轉二進制
        let binaryElement = element.charCodeAt().toString(2)
        //由於js原生方法轉二進制如果前面是0可能會不滿8位,所以前面補0,轉爲8位的對應ascii碼二進制
        binaryElement = binaryElement.length === 8 ? binaryElement : ('0' + binaryElement)  //不足8位的二進制碼在前面補0
        tempResult.push(binaryElement);
    });
    let index = 0;
    // 不滿3個字符往後面補滿3個字符(3個字符(24個二進制位)是6和8的最小公倍數)
    while(tempResult.length % 3 != 0){
        tempResult.push('00000000')
    }
    console.log(tempResult.length)
    return tempResult.join('');
}
let binary = toBinary('asdasds');

那麼就是第一步和第二步實現了

二進制轉 base64字符串

//將字符串存爲數組
let KEYCODE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".split(''); 

function toBase64 (binary){
    console.log(binary);
    let tempResult = [];
    let result = [];
    let index = 0;
    // 每6位切割二進制
    while(index+6 < binary.length){
        tempResult.push(binary.slice(index,index+6))
        index = index + 6 ;
    }
    //不滿6位的前面補0
    console.log(binary.slice(index,index+6))
    tempResult.push(("000000" + binary.slice(index,index+6)).substr( -6 ));
    tempResult.forEach(element => {
    	//將二進制轉爲數組下標
        let index = parseInt(element,2);
        //獲取對應下標字符串
        result.push(index === 0 ? '=' : KEYCODE[index])
    });
    //字符串拼接
    return result.join('')
}
let a = toBase64(binary);
console.log(a);

//  YXNkYXNkcw==

到這裏基本就實現了,結果跟原生的方法打印的是一樣的

但是也存在一些問題和改進

  • 對於中文字符和特殊字符的支持

    javascript中的中文都是默認utf-16編碼,但是網頁中編碼格式基本都是UTF-8,然而即便我們用UTF-8格式保存了HTML文件,但是其中的中文字符依然是以UTF-16的形式保存的。所以我們首先要將中文字符轉化爲utf-8,然後再轉二進制,最後即可用上面的方法進行編碼
    代碼如下:

    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個字節的範圍,則直接寫入
        if (code >= 0x00 && code <= 0x7f) {
            byteSize += 1;
            utf8Arr.push(code);
    
            //如果碼值是2個字節以上的範圍,則按規則進行填充補碼轉換
        } 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('')
    }
    
  • 如何對圖片base64編碼進行實現

    圖片的話,要用到canvas ,將圖片轉換爲二進制流,然後再掉用上述的編碼方法


下一次

  • 可以嘗試圖片的base64編碼
  • 可以做解碼過程
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章