Base64 就是一种编码方法,可以将任意值转成 0~9、A~Z、a-z、+和/这64个字符组成的可打印字符。
编码原理
Base64是一种索引编码,每个字符都对应一个索引。字符索引关系表具体如下述数组,数组中成员的下标值即对应的索引。
['A', 'B', 'C', ... 'a', 'b', 'c', ... '0', '1', ... '+', '/']
0 1 2 ... 26 27 28 ... 52 53 ... 62 63
下述两种方法都可以生成此字符索引关系数组:
- 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('')
- [].slice.call('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/')
编码规则
- 将待转的字符串每三个字节分一组,每个字节转为Unicode 码点(二进制表示)对应的是8比特(bit),3个字节一共24个二进制位
- 24个二进制位重新分组,每6个一组,共4组
- 每组高位补俩0,此时24变32,共四个字节
- 每个字节转为十进制得到索引编号,根据字符索引关系表,得到一个四个字节的Base64编码字符。
举例
- Hel分别对应的ASCII码为72,101,108(可通过字符串的charCodeAt方法获取)
'H'.charCodeAt('0') // 72 'e'.charCodeAt('0') // 101 'l'.charCodeAt('0') // 108
(72).toString(2).padStart(8,'0') // '01001000' (101).toString(2).padStart(8,'0') // '01100101' (108).toString(2).padStart(8,'0') // '01101100'
- 24个二进制位重新分组,每6个一组,共4组
- 每组高位补俩0,此时24变32,共四个字节,即00010010,00000110,00010101,00101100。
这四个字节对应的十进制即索引编码为:18、6、21、44(可通过0bxxxxxxxx方式转为十进制)0b00010010 // 18 0b00000110 // 6 0b00010101 // 21 0b00101100 // 44
- 对照字符索引关系表,通过下标找到对应的字符。
const str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; // const arr = str.split(''); const arr = Array.prototype.slice.call(str); arr[18] // 'S' arr[6] // 'G' arr[21] // 'V' arr[44] // 's'
因此: 'Hel' 经Base64编码后得到: 'SGVs'
位数不足的情况
待转字符不总是3的倍数,当字节数不足3时,又该如何?
和三个字节的差异主要体现在重新分组和补位上。
两个字节:共16个二进制位,每6个一组时,第三组不足的两位用0补齐,得到三个编码字符,最后一位用'='补上。'hi' 经Base64编码后得到: 'aGk='。
一个字节:共8个二进制位,每6个一组时,第二组不足的四位用0补齐,得到两个编码字符,最后两位用'='补上。'x' 经Base64编码后得到: 'eA=='。
解码
解码是编码的逆向操作,理解了编码,解码还是比较容易的。这里不展开说明。
编解码方法
1、JavaScript 原生提供两个 Base64 相关的方法
- btoa() :任意值转为 Base64 编码
- atob() :Base64 编码转为原来的值
btoa('Hello World!') // "SGVsbG8gV29ybGQh"
atob('SGVsbG8gV29ybGQh') // "Hello World!"
这两个方法不适合非 ASCII 码的字符,会报错。
btoa('中') // 报错
要将非 ASCII 码字符转为 Base64 编码,必须中间插入一个转码环节,再使用这两个方法。
btoa(encodeURIComponent('中')) // 'JUU0JUI4JUFE'
decodeURIComponent(atob('JUU0JUI4JUFE')) // '中'
注:如果你的项目要兼容IE6~9,请放弃这俩方法,浏览器不支持。
2、开源的base64
// 下载依赖
npm install --save js-base64
// 使用
import {Base64} from 'js-base64'
Base64.encode('Hello world!') // 'SGVsbG8gd29ybGQh'
Base64.decode('SGVsbG8gd29ybGQh') // 'Hello world!'
Base64.encode('中') // '5Lit'
Base64前端应用
1、Base64代替小图标
一些比较小的图标,使用 Base64 编码。浏览器能直接展示Base64编码图片,可以减少 http 请求。
<img data-v-e43c18bc="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAAC..." alt="">
.icon-camera {
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAmCAYAAACCjRgBAAAAAXNS...) left top no-repeat;
}
img的src属性或者 background的url属性,指向的不是一个资源地址,而是Data URLs(前缀为 data:
协议的 URL,其允许内容创建者向文档中嵌入小文件)。
// 语法
Data URLs 由四个部分组成:前缀 (data:)、指示数据类型的 MIME 类型、如果非文本则为可选的base64标记、数据本身
data:[<mediatype>][;base64],<data>
Base64特点是编码后体积会变大【至少会增加1/3】。因此,图标编码后会比原图大,图片越大,差别就越大,需约定转码上限。Vue项目中可通过url-loader来配置图标转Base64的大小。
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000, // 单位B, 约等于10Kb
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
}
2、Vue路由参数的编解码
// 编码
this.$router.push({
path: '....',
query: {
info: Base64.encode(JSON.stringify(param))
}
})
// 解码
created() {
let param = JSON.parse(Base64.decode(this.$route.query.info))
}
3、FileReader 对象
FileReader 对象用于读取 File 对象或 Blob 对象所包含的文件内容。
浏览器原生提供一个FileReader构造函数,用来生成 FileReader 实例。
FileReader.readAsDataURL():读取完成后,result属性将返回一个 Data URL 格式(Base64 编码)的字符串,代表文件内容。对于图片文件,这个字符串可以用于<img>元素的src属性。注意,这个字符串不能直接进行 Base64 解码,必须把前缀data:*/*;base64,从字符串里删除以后,再进行解码。
/* HTML 代码如下
<input type="file" onchange="previewFile()">
<img src="" height="200">
*/
function previewFile() {
var preview = document.querySelector('img');
var file = document.querySelector('input[type=file]').files[0];
var reader = new FileReader();
reader.addEventListener('load', function () {
preview.src = reader.result;
}, false);
if (file) {
reader.readAsDataURL(file);
}
}
上面代码中,用户选中图片文件以后,脚本会自动读取文件内容,然后作为一个 Data URL 赋值给<img>元素的src属性,从而把图片展示出来。
The End
参考连接: