一. Base64编码由来javascript
为何会有Base64编码呢?由于有些网络传送渠道并不支持全部的字节,例如传统的邮件只支持可见字符的传送,像ASCII码的控制字符就不能经过邮件传送。这样用途就受到了很大的限制,好比图片二进制流的每一个字节不可能所有是可见字符,因此就传送不了。最好的方法就是在不改变传统协议的状况下,作一种扩展方案来支持二进制文件的传送。把不可打印的字符也能用可打印字符来表示,问题就解决了。Base64编码应运而生,Base64就是一种基于64个可打印字符来表示二进制数据的表示方法。java
二. Base64编码原理canvas
咱们的图片大部分都是能够转换成base64编码的data:image。 这个在将canvas保存为img的时候尤为有用。现代浏览器都已经支持原生的基于base64的encode和decode,例如btoa和atob浏览器
看一下Base64的索引表,字符选用了"A-Z、a-z、0-九、+、/" 64个可打印字符。数值表明字符的索引,这个是标准Base64协议规定的,不能更改。64个字符用6个bit位就能够所有表示,一个字节有8个bit位,剩下两个bit就浪费掉了,这样就不得不牺牲一部分空间了。这里须要弄明白的就是一个Base64字符是8个bit,可是有效部分只有右边的6个bit,左边两个永远是0。bash
那么怎么用6个有效bit来表示传统字符的8个bit呢?8和6的最小公倍数是24,也就是说3个传统字节能够由4个Base64字符来表示,保证有效位数是同样的,这样就多了1/3的字节数来弥补Base64只有6个有效bit的不足。你也能够说用两个Base64字符也能表示一个传统字符,可是采用最小公倍数的方案实际上是最减小浪费的。结合下边的图比较容易理解。Man是三个字符,一共24个有效bit,只好用4个Base64字符来凑齐24个有效位。红框表示的是对应的Base64,6个有效位转化成相应的索引值再对应Base64字符表,查出"Man"对应的Base64字符是"TWFU"。说到这里有个原则不知道你发现了没有,要转换成Base64的最小单位就是三个字节,对一个字符串来讲每次都是三个字节三个字节的转换,对应的是Base64的四个字节。这个搞清楚了其实就差很少了。网络
可是转换到最后你发现不够三个字节了怎么办呢?愿望终于实现了,咱们能够用两个Base64来表示一个字符或用三个Base64表示两个字符,像下图的A对应的第二个Base64的二进制位只有两个,把后边的四个补0就是了。因此A对应的Base64字符就是QQ。上边已经说过了,原则是Base64字符的最小单位是四个字符一组,那这才两个字符,后边补两个"="吧。其实不用"="也不耽误解码,之因此用"=",多是考虑到多段编码后的Base64字符串拼起来也不会引发混淆。因而可知Base64字符串只可能最后出现一个或两个"=",中间是不可能出现"="的。下图中字符"BC"的编码过程也是同样的。ide
咱们来看看,在javascript中如何使用Base64转码ui
var str = 'javascript';
window.btoa(str)
//转码结果 "amF2YXNjcmlwdA=="
window.atob("amF2YXNjcmlwdA==")
//解码结果 "javascript"
复制代码
二.对于转码来讲,Base64转码的对象只能是字符串,所以来讲,对于其余数据还有这必定的局限性,在此特别须要注意的是对Unicode转码。this
var str = "China,中国"
window.btoa(str)
复制代码
Uncaught DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.编码
很明显,这种方式是不行的,那么如何让他支持汉字呢,这就要使用window.encodeURIComponent和window.decodeURIComponent
var str = "China,中国";
window.btoa(window.encodeURIComponent(str))
//"Q2hpbmElRUYlQkMlOEMlRTQlQjglQUQlRTUlOUIlQkQ="
window.decodeURIComponent(window.atob('Q2hpbmElRUYlQkMlOEMlRTQlQjglQUQlRTUlOUIlQkQ='))
//"China,中国"复制代码
function Base64() {
// private property
_keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
// public method for encoding
this.encode = function (input) {
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = _utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
_keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
_keyStr.charAt(enc3) + _keyStr.charAt(enc4);
}
return output;
}
// public method for decoding
this.decode = function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = _keyStr.indexOf(input.charAt(i++));
enc2 = _keyStr.indexOf(input.charAt(i++));
enc3 = _keyStr.indexOf(input.charAt(i++));
enc4 = _keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
output = _utf8_decode(output);
return output;
}
// private method for UTF-8 encoding
_utf8_encode = function (string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
// private method for UTF-8 decoding
_utf8_decode = function (utftext) {
var string = "";
var i = 0;
var c = c1 = c2 = 0;
while ( i < utftext.length ) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
} else if((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i+1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
} else {
c2 = utftext.charCodeAt(i+1);
c3 = utftext.charCodeAt(i+2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
}
复制代码
三. 总结
提及Base64编码可能有些奇怪,由于大多数的编码都是由字符转化成二进制的过程,而从二进制转成字符的过程称为解码。而Base64的概念就刚好反了,由二进制转到字符称为编码,由字符到二进制称为解码。
Base64编码主要用在传输、存储、表示二进制等领域,还能够用来加密,可是这种加密比较简单,只是一眼看上去不知道什么内容罢了,固然也能够对Base64的字符序列进行定制来进行加密。
Base64编码是从二进制到字符的过程,像一些中文字符用不一样的编码转为二进制时,产生的二进制是不同的,因此最终产生的Base64字符也不同。例如"上网"对应utf-8格式的Base64编码是"5LiK572R",对应GB2312格式的Base64编码是"yc/N+A=="。