一、前端的文件下载需要转换成二进制在下载。实现文件下载的几个方法:
(1)使用Blob对象,创建二进制文件,Blob对象接受两种类型,一种是字符串,一种是二进制片段。
// 前端的文件下载 不是文件 需要转换成二进制在下载
// let str = `<div>hellow zf</div>`;
// 转换成二进制 文件类型可以存储文件内容 文件的所有呃逆荣 Bol
// 接受的可以是二进制片段也可以是字符串 说明文件的类型
// 创建二进制文件
// const blob = new Blob([str],{
// type:'text/html'
// })
// // 根据文件创建临时的连接 URL.createObjectURL
// const a = document.createElement('a');
// a.setAttribute('download','index.html');
// a.href = URL.createObjectURL(blob);
// a.click()
// 把路径释放掉
// URL.revokeObjectURL()
创建blob实例,类型是text/html,生成二进制文件,然后创建a标签,在a标签上面创建属性download等于index.html,使用URL属性的createObjectURL方法创建blob协议的url,在a的href上面增加这个临时的url,手动调用a的click方法,最后要使用revokeObjectURL把路径释放掉。
(2)前端可以使用h5实现二进制的读取,我们使用FileReader构造函数,来读取文件。
avator.addEventListener('change',(e)=>{
let file = e.target.files[0];
// console.log('file',file)
// Blob是文件 而文件是由二进制组成的 buffer
// File对象是Blob对象的子集
// h5 读取文件对象 兼容到ie9+
// 创建读取文件对象
// let fileReader = new FileReader();
// // 文件读取完成以后
// fileReader.onload = function(){
// let img = document.createElement('img');
// // 读取的结果在对象的result上面
// img.src = fileReader.result;
// document.body.appendChild(img)
// }
// fileReader.readAsDataURL(file) ;// dataUrl 就是 base64所有文件都可以转成base64
// 这种方式不提倡 因为是异步的
// 转成异步的方法
let img = document.createElement('img');
// URL.createObjectURL 创建URL对象,当结束使用某个URL对象以后应该用revokeObjectURL方法让浏览器知道不用在内存中继续保留这个对这个文件的一引用了
let url = URL.createObjectURL(file);
img.src = url;
document.body.appendChild(img)
URL.revokeObjectURL(url);
})
input类型是file,添加的文件类型是File对象,这个File对象就是Blob对象的子类,生成的是一个二进制文件buffer,使用new FileReader() 创建fileReader读取文件的对象,使用readAsDataURL方法读取文件将二进制文件转变成base64编码,文件读取完成以后创建img,将对象的result上面赋值给图片的src上面,并且将创建的图片放置到body上面。
二、前端编码的发展历程。
(1)最开始的ACSII编码是由一个字节组成的,一个字节是由8位组成的,可以表示一个字符和一个字母,最大可以转换成255.
(2)然后有了gb2312编码,一个汉字由两个字节组成。
(3)gbk–》gb18030
(4)组织unicode-》使用utf8一个汉字由三个字节组成((js使用的是utf16)
三、ArrayBuffer对象用来表示通用的、固定长度的原始二进制数据缓冲区,单位是字节。
(1) 你不能直接操作 ArrayBuffer 的内容,而是要通过类型数组对象或 DataView 对象来操作,它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区的内容。
// 创建4个字节的二进制数组
const arrayBuffer = new ArrayBuffer(4)
console.log(arrayBuffer)
let x = new Uint8Array(arrayBuffer);
// 一个字节是8个位 组成的数组 所以这个是长度为4 的数组
// 如果单位是一个字节是16位 那么这个数组就是长度为2 的数组
// new ArrayBuffer 创建四个字节的二进制
x[0] = 1; // 这个0代表的是从后向前的第几组的十进制转换为
// 一个字节最大是255
// ArrayBuffer 对象用来表示通用的、固定长度的原始二进制数据缓冲区 单位是字节
// 你不能直接操作 ArrayBuffer 的内容,而是要通过类型数组对象或 DataView 对象来操作,它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区的内容。
// /Uint16Array 比如这个就表示 单位是每组16个位 4个字节是32位 那么一共是 2组 每组最大的十进制是 2 ** 16 - 1
x[1] = 255; // 给类型数组对象赋值的时候是从前往后赋值而且赋值是十进制的,真正转换是是从后面开始转换成二进制 比如 x[0] = 1 就相当于 00000000 00000000 11111111 00000001
// 默认不赋值就是0
console.log(x)
let x2 = new Uint16Array(arrayBuffer);
console.log(x2) // [65281, 0]
let x3 = new Uint32Array(arrayBuffer)
console.log(x3)
四、js中有进制转换的方法。
// 任意进制转换10进制
// console.log(parseInt('11111111',2))
// js可以识别浮点类型会认为后面的是小数点
// console.log(255.0.toString(16)) // 字符串类型 可以将任意进制转换成任意进制 十进制转换成其他进制
任意进制转换成10十进制使用parseInt方法,任意进制转换成任意进制使用toString方法。
为什么0.1 + 0.2 !== 0.3 ,因为将小数转换成2进制 乘以2 取整数 算出来会大一些,取二进制是无线循环的,由于内存的原因会取固定的长度然后进位这样会比实际值大。
四、base64转码的过程,使用node中的Buffer对象生成16进制的编码。实际上是生成2进制的但是由于2进制太长所以转换成16进制
// 汉字是由三个字节组成 但是不能用二进制来表示太长了
// node不支持gbk编码 只支持utf8编码
let r = Buffer.from('珠'); // e7 8f a0 3*8的格式
console.log(r)
// base64转换规则 转码
// 1.16进制转成2进制
// 再utf8编码中一个汉字是三个字节
console.log(0xe7.toString(2)) //11100111
console.log(0x8f.toString(2)) //10001111
console.log(0xa0.toString(2)) //10100000
// 转换成base64 是 每6位一组 一共四组
// 将 111001 111000 111110 100000 转换成 111001 1110001 111110 100000
// base64转换规则 将2进制转换成10进制找到对应的规则
// utf8是node支持的编码 1个汉字是由3个字节组成
// 英文按照ascii 编码方法 用一个字节表示
console.log(parseInt('111001',2))
console.log(parseInt('111000',2))
console.log(parseInt('111110',2))
console.log(parseInt('100000',2))
let str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
str += str.toLowerCase();
str += '0123456789+/'
console.log(str[57]+str[56]+str[62]+str[32])
// 57
// 56
// 62
// 32
// 二进制转换成base64字符串
// Buffer提供了转换的方式
// toSting可以将任意进制转换成任意进制 或者是任意编码
let base64 = Buffer.from('珠').toString('base64');
console.log('base64',base64)
过程是将汉字使用Buffer转换成16进制,再讲16进制转换成2进制,由于汉字是由3个字节组成的,3个字节是由24位组成,base64是6位4组组成的所以转换成 111001 1110001 111110 100000 格式。然后在使用base64的编码方式
let str = ‘ABCDEFGHIJKLMNOPQRSTUVWXYZ’;
str += str.toLowerCase();
str += ‘0123456789+/’
找到对应的位置生成base64的编码54+g
let base64 = Buffer.from(‘珠’).toString(‘base64’);
console.log(‘base64’,base64)
也可以使用toString方法转换成base64格式
五:Buffer的定义:
当处理像TCP流或文件流时,必须使用到二进制数据。因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。一个 Buffer 类似于一个整数数组,但它对应于 V8 堆内存之外的一块原始内存。
- Buffer表示当前的内存信息,用二进制表示的,服务器可以操作二进制。
- Buffer可以和字符串相互转换。
- Buffer 中所有声明的数字都是字节为单位的。
- Buffer声明后的大小是固定的,声明出来以后不能随意的改变。
Buffer相关的API:
- var buffer = Buffer.alloc(6); // 声明一个字节大小为6的二进制缓存区。
- var buffer = Buffer.from(‘珠峰架构’) // 将字符转换成二进制缓存区。默认采用utf8编码 <Buffer e7 8f a0 e5 b3 b0 e6 9e b6 e6 9e 84>
- var buffer = Buffer.from([1,2,3,4,0xbd]) 接收数组格式的,将这个数组转换成Buffer二进制缓存区(默认显示的是16进制表示的,因为2进制太长),这个缓存区的长度是数组的长度,每个值超过1个字节会变成00。
- var buffer = Buffer.from(‘珠峰架构’) var str = buffer.toString(‘utf8’) // 默认不传递的话就是utf8格式,还可以转换成base64格式。可以将二进制码转换成字符串 珠峰架构。
插入buffer是怎么生成16进制数的
比如:珠峰架构这几个字,首先会查找每一个汉字的Unicode的编码找到对应的范围:
Unicode符号范围 | UTF-8编码方式
(十六进制) | (二进制)
----------------------±--------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
----------------------±--------------------------------------------
然后将Unicode的编码转换成2进制,然后对应Unicode符号范围 ,将二进制码填入到对应的UTF-8编码中。如果是一个字节比如数字,字母的话Unicode和ACSII是对应。他的buffer就是Unicode码,如果大于1个字节那么将对应的二进制码,从后向前面补充到UTF-8编码中,不足的在前面补0。
buffer的其他方法:
- buffer.copy(buf,0,0,6) // buffer的拷贝方法,被拷贝的buffer对象.copy(拷贝到的对象,拷贝到对象开始的字节位置,被拷贝对象的开始字节,被拷贝对象的结束字节),这个方法很少用,常用的是concat方法。
var buf = Buffer.alloc(12);
var buffer1 = Buffer.from('珠峰')
var buffer2 = Buffer.from('架构')
buffer1.copy(buf,0,0,6)
buffer2.copy(buf,6,0,6)
- 我们来看copy方法的源码实现
Buffer.prototype.copy = function(targetBuffer,targetStart,sourceStart=0,soureEnd=this.length){
// this指的是被拷贝的buffer对象 这里默认拷贝开始的索引是0字节 结束的位置是被拷贝对象的长度
for(let i = sourceStart;i < sourceEnd;i++){
targetBuffer[targetStart++] = this[i]
}
}
- 下一个合并的方法concat,这个方法是Buffer的静态方法,const newBuffer = Buffer.concat([buffer1,buffer2],100) // 接受两个参数第一个参数是一个数组要合并的buffer数组,第二个是要合并的数组的长度,超出的话就合并数组真实的长度,最后返回的是一个新的Buffer实例对象,concat内部实现方法其实使用了copy方法来实现的,我们来看源码。
Buffer.concat = function(bufferList,length=bufferList.reduce((prev,cur)=>{
// 返回的是当前bufferList真实的长度
return prev + cur.length
},0)){
let buf = Buffer.alloc(length); //声明一个长度为length的buffer对象
let offset = 0; // 拷贝的目标对象的开始索引
bufferList.forEach((bufItem)=>{
bufItem.copy(buf,offset)
offset += bufItem.length;
})
// 如果超出真实的长度 需要截取当前buffer对象
return buf.slice(0,offset);
}
- buffer还有一个方法就是和数组很像的方法,判断是否是buffer类型,var isBuffer = Buffer.isBuffer(buffer); // 判断当前对象是否是buffer对象。