Appearance
前言
最近弄公司的大文件上传,实现了分片、暂停、继续、断点续传和秒传,记录一些过程中的细节。
Buffer
Buffer是二进制数据的操作对象,通过操作这个对象来操作二进制数据。简单理解就是一个数组,数组的每一项是一个二进制。任何数据在底层都是二进制的格式,所以buffer也能和任意数据相互转换。
ts
const buf = new Buffer("ldsfdld", 'utf-8') // 不同编码会解析成不同二进制,详情文章编码的故事
console.log(buf) // Buffer<00 00 xx xx ...> 每一个是
buf.toString() // 转换成字符串
buf.toJSON() // { type: 'buffer', data: [00, 00, xx] } 比如00 就是l,具体看buffer的编码格式可以看到buffer是没有格式,一堆二进制别人也不知道是干嘛的,所以浏览器就有ArrayBuffer这个东西。ArrayBuffer表示一个固定长度的,通用的二进制数据,不能被读写,只能被其他工具操作,比如TypeArray和DataView。nodejs中buffer我们可以看到是可以读写的,nodejs中buffer就相当于ArrayBuffer和操作类二合一。
为什么ArrayBuffer不能被操作?一堆二进制没办法判断从哪儿开始断开,有了对应的读写工具就能知道怎么断句来解析出最后的内容
TypeArray
TypeArray提供了解析Buffer的接口,通过TypeArray能够简化对ArrayBuffer的读取。有以下几种解析方式https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/TypedArray
TypeArray对于二进制处理是全局的,所有片段都按照这个格式解析。
ts
const buf = new ArrayBuffer(8) // 申请一个8字节的空间
const int8Array = new IntArray8(buf) // 每8位按照有符号整数解析(第一位是符号位所以是 -+ 2 ^ 7 - 1 ) 大小
console.log(int8Array.length) // 刚好8个
const uint32Array = new Uint32Array(buf) // 按照32位无符号整数来解析, 0 ~ 2 ^ 32 - 1
console.log(uint32Array.length) // 2 buf 一共64位,uint32Array一项是32位。
uint32Array.buffer // 获取原始的buffer想一想为什么都是整数型的,没有其他形式。
DataView
上面是将所有片段都按照既定的大小和格式处理,DataView是一种灵活的数据接口,不用考虑字节序(读取buffer时从左到右还是从右到左,比如 0x12345678, 按照 12345678读取还是87654321读取)。正是因为其灵活性,所以需要指定需要读取的字节起始位和字节长度,注意是字节不是位。
ts
const dataView = new DataView(buffer, [, byteOffset [, byteLength]])
dataView.setUnit8(1, -2) // 从位置1的字节开始,设置一个8位无符号的整数,值是2 [0000 0010] 变成这
dataView.getInt8(1) // 从位置1的字节开始,按照8位有符号的整数读取一个值,返回是[0000 00010], 结果是2,
data.View.buffer // 获取原始的bufferBlob
Blob是一种特殊的二进制对象,里面还保存了mime type的元数据。大概结果是{type: 'xxx', buffer: '00 01 ab' }这样就不需要用TypeArray或者DataView解析二进制数据。blob需要解决两个问题,buffer从哪儿来,代表的是什么mime 类型。new Blob(buffer, ArrayBuffer, DomString)
ts
const myBlob = new Blob(someBuffer, { type: 'application', endings: 'transparent'}) //blob是一个buffer对象(不是单纯的二进制),需要使用特定的接口才能读取。FileReader 是一个能够对缓冲区数据或者文件进行读取的对象,能够按照不同格式来读取buffer,下面是抄的别人的代码https://juejin.cn/post/7093908575935807502 同时做了一些改造
ts
const name = JSON.stringify({
name: '19QIngfeng'
});
// 传入DOMString创建blob, DomString会按照UTF-8进行编码到二进制
const blob = new Blob([name], {
type: 'application/json',
});
/**
*
* @param {*} blob blob 对象
* @param {*} type 输出的结果
*/
function getBlobByType(blob, type) {
const fileReader = new FileReader(blob);
switch (type) {
// 读取文件的 ArrayBuffer 数据对象.
case 'arrayBuffer':
// 等同于将19QIngfeng按照utf-8编码写入到ArrayBuffer中
fileReader.readAsArrayBuffer(blob);
break;
// 读取文件为的字符串
case 'DOMstring':
fileReader.readAsText(blob, 'utf8');
break;
// 读取文件为data: URL格式的Base64字符串
case 'dataUrl':
// 考虑blob中type值,将二进制转换成base64位的数据
fileReader.readAsDataURL(blob);
break;
// 读取文件为文件的原始二进制数据(已废弃不推荐使用)
case 'binaryString':
fileReader.readAsBinaryString(blob);
break;
default:
break;
}
return new Promise((resolve) => {
// 当文件读取完成时候触发
fileReader.onload = (e) => {
// 获取最终读取结果
const result = e.target.result;
resolve(result);
};
});
}
// ArrayBuffer 对象
getBlobByType(blob, 'arrayBuffer').then((res) => console.log(res));
// {"name":"19QIngfeng"}
getBlobByType(blob, 'DOMstring').then((res) => console.log(res));
// data:application/json;base64,eyJuYW1lIjoiMTlRSW5nZmVuZyJ9
getBlobByType(blob, 'dataUrl').then((res) => console.log(res));
// {"name":"19QIngfeng"}
getBlobByType(blob, 'binaryString').then((res) => console.log(res));File
File是Blob的高级对象,实现了Blob的特性。增加了name和options信息。
name:文件名 size:文件大小 lastModified :最后修改时间(时间戳) lastModifiedDate:最后修改时间Data对象 type:MIME类型 File对象方法 继承自Blob的slice方法 其余和Blob差不多。File的来源除了构造函数外,能够直接从input框获取 document.getElementById('fileItem').files[0];
Object url
在上传图片本地预览时,直接图片使用读取成File,通过Url.createObjectUrl(File | blob | MediaSource)创建一个只在当前实例页面存在的url,在将这个url放置到image标签就像一条后端的资源展示到界面上。
base:64转回二进制。
ts
function dataURLtoBlob(dataurl) {
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
}