本文屬於 字符編碼系列文章之一,更多請前往 字符編碼系列。html
Base64是一種編碼方式,一般用於將二進制數據編碼爲可打印字符組成的數據格式。git
在好久之前,發送郵件時只支持ASCII字符的發送,若是有非ASCII碼字符,則發送不了,因而須要在不改變傳統協議的狀況下,作一種擴展方案來支持這類字符的傳送。Base64編碼應運而生。github
不少開發者喜歡直接用Base64進行加密解密工做,實際上這個是徹底無心義的,由於Base64這種編碼規則是公開的,基本只要有程序能力都能解開,因此請勿用做加密用途。segmentfault
Base64編碼的主要的做用不在於安全性,而在於讓內容能在網絡間無錯的傳輸。(經常使用語編碼特殊字符,編碼小型二進制文件等)安全
將數據按照 3個8位字節一組的形式進行處理,每3個8位字節在編碼以後被轉換爲4個6位字節服務器
3*8=24
變爲4*6=24
當數據的長度沒法知足3的倍數的狀況下,最後的數據須要進行填充操做網絡
=
做爲填充字符(這裏=
不是第65個字符,僅僅作填充做用)=
號進行填充是爲了解碼時方便還原(由於=
號只須要還原爲0
便可)=
號還原爲0
便可每6個單元高位補2個零造成的字節位於0~63之間,經過在轉碼錶中查找對應的可打印字符。「=」用於填充。以下所示爲轉碼錶。編碼
以」Word」字符串的編碼和解碼爲例。加密
├ 原始字符 | W | o | r | d(因爲不是3的倍數,因此要補0) | ├─────────────────────────────────| ├ ASCII碼 | 87 | 111 | 114 | 100 | ├─────────────────────────────────| ├ 8bit字節 | 01010111 | 01101111 | 01110010 | 01100100 | 00000000 | 00000000 | ├─────────────────────────────────| ├ 6bit字節 | 010101 | 110110 | 111101 | 110010 | 011001 | 000000 | 000000 | 000000 | ├─────────────────────────────────| ├ B64十進制 | 21 | 54 | 61 | 50 | 25 | 0(注意,這裏有兩位是d裏面的,因此是正常的0) | 異常(須要補上=號) | 異常 | ├─────────────────────────────────| ├ 對應編碼 | V | 2 | 9 | y | Z | A | = | = | └───────────────────────────────────────────
因此’Word’的編碼結果是V29yZA==spa
├ 原始編碼 | V | 2 | 9 | y | Z | A | = | = | ├─────────────────────────────────| ├ B64十進制 | 21 | 54 | 61 | 50 | 25 | 0 | 異常 | 異常 | ├─────────────────────────────────| ├ 6bit字節 | 010101 | 110110 | 111101 | 110010 | 011001 | 000000 | 000000 | 000000 | ├─────────────────────────────────| ├ 8bit字節 | 01010111 | 01101111 | 01110010 | 01100100 | 00000000 | 00000000 | ├─────────────────────────────────| ├ ASCII碼 | 87 | 111 | 114 | 100 |異常 |異常 | ├─────────────────────────────────| ├ 對應字符 | W | o | r | d | 無 | 無 | └─────────────────────────────────────
因而可知,解碼過程就是編碼過程的逆過程。
須要注意的是,實際編碼時須要注意程序內部的編碼,例如Javascript內置的編碼如今能夠當作是UTF-16,因此若是編碼成GBK或UTF-8時須要通過必定的轉換
/** * @description 建立一個base64對象 */ (function(base64) { /** * Base64編碼要求把3個8位字節(3*8=24)轉化爲4個6位的字節(4*6=24), * 以後在6位的前面補兩個0,造成8位一個字節的形式。 * 因爲2的6次方爲64, 因此每6個位爲一個單元, 對應某個可打印字符。 * 當原數據不是3的整數倍時, 若是最後剩下兩個輸入數據, * 在編碼結果後加1個「=;若是最後剩下一個輸入數據,編碼結果後加2個「=; * 若是沒有剩下任何數據,就什麼都不要加,這樣才能夠保證資料還原的正確性。 */ /** * base64轉碼錶,最後一個=號是專門用來補齊的 */ var keyTable = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; /** * @description 將一個目標字符串編碼爲base64字符串 * @param {String} str 傳入的目標字符串 * 能夠是任何編碼類型,傳入什麼類型就輸出成了什麼樣的編碼 * 因爲js內置是utf16編碼,而服務器端通常不使用這種, * 因此傳入的編碼通常是採起utf8或gbk的編碼 * @return {String} 編碼後的base64字符串 */ function encodeBase64(str) { if (!str) { return ''; } // 遍歷索引 var i = 0; var len = str.length; var res = []; var c1, c2, c3 = ''; // 用來存對應的位置 var enc1, enc2, enc3, enc4 = ''; while (i < len) { c1 = str.charCodeAt(i++) & 0xFF; c2 = str.charCodeAt(i++); c3 = str.charCodeAt(i++); enc1 = c1 >> 2; enc2 = ((c1 & 0x3) << 4) | ((c2 >> 4) & 0x0F); enc3 = ((c2 & 0x0F) << 2) | ((c3 & 0xC0) >> 6); enc4 = c3 & 0x3F; // 專門用來補齊=號的 if (isNaN(c2)) { enc3 = enc4 = 0x40; } else if (isNaN(c3)) { enc4 = 0x40; } res.push(keyTable.charAt(enc1)); res.push(keyTable.charAt(enc2)); res.push(keyTable.charAt(enc3)); res.push(keyTable.charAt(enc4)); c1 = c2 = c3 = ""; enc1 = enc2 = enc3 = enc4 = ""; } return res.join(''); }; /** * @description 解碼base64字符串,還原爲編碼前的結果 * @param {String} str 傳入的目標字符串 * 能夠是任何編碼類型,傳入什麼類型就輸出成了什麼樣的編碼 * 因爲js內置是utf16編碼,而服務器端通常不使用這種, * 因此傳入的編碼通常是採起utf8或gbk的編碼 * @return {String} 編碼後的base64字符串 */ function decodeBase64(str) { if (!str) { return ''; } // 這裏要判斷目標字符串是否是base64型,若是不是,直接就不解碼了 // 兩層判斷 if (str.length % 4 != 0) { return ""; } var base64test = /[^A-Za-z0-9\+\/\=]/g; if (base64test.exec(str)) { return ""; } var len = str.length; var i = 0; var res = []; var code1, code2, code3, code4; var c1, c2, c3 = ''; while (i < len) { code1 = keyTable.indexOf(str.charAt(i++)); code2 = keyTable.indexOf(str.charAt(i++)); code3 = keyTable.indexOf(str.charAt(i++)); code4 = keyTable.indexOf(str.charAt(i++)); c1 = (code1 << 2) | (code2 >> 4); c2 = ((code2 & 0xF) << 4) | (code3 >> 2); c3 = ((code3 & 0x3) << 6) | code4; res.push(String.fromCharCode(c1)); if (code3 != 64) { res.push(String.fromCharCode(c2)); } if (code4 != 64) { res.push(String.fromCharCode(c3)); } } return res.join(''); }; /** * @description 將字符串進行base64編碼,以後再進行uri編碼 * @param {String} str 傳入的utf16編碼 * @param {String} type 類別,是utf8,gbk仍是utf16,默認是utf16 * @param {Boolean} isUri 是否uri編碼 * @return {String} 編碼後並uri編碼的base64字符串 */ base64.encode = function(str, type, isUri) { type = type || 'utf16'; if (type == 'gbk') { // 轉成 gbk str = exports.utf16StrToGbkStr(str); } else if (type == 'utf8') { // 轉成 utf8 str = exports.utf16StrToUtf8Str(str); } // 不然就是默認的utf16不要變 // 先b64編碼,再uri編碼(防止網絡傳輸出錯) var b64Str = encodeBase64(str); if (isUri) { b64Str = encodeURIComponent(b64Str); console.log(b64Str); } return b64Str; }; /** * @description 將字符串先進行uri解碼,再進行base64解碼 * @param {String} str 傳入的編碼後的base64字符串 * @param {String} type 類別,是utf8,gbk仍是utf16,默認是utf16 * @param {Boolean} isUri 是否uri解碼 * @return {String} 編碼後並uri編碼的base64字符串 */ base64.decode = function(str, type, isUri) { type = type || 'utf16'; if (isUri) { str = decodeURIComponent(str); } var decodeStr = decodeBase64(str); if (type == 'gbk') { return exports.gbkStrToUtf16Str(decodeStr); } else if (type == 'utf8') { return exports.utf8StrToUtf16Str(decodeStr); } // 不然就是默認的utf16不要變 return decodeStr; }; })(exports.Base64 = {});
詳細能夠參考源碼: https://github.com/dailc/charset-encoding-series
初次發佈2017.06.10
於我的博客
http://www.dailichun.com/2017/06/10/base64encoding.html