本文属于 字符编码系列文章之一,更多请前往 字符编码系列。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