Skip to content

前言

最近弄公司的大文件上传,实现了分片、暂停、继续、断点续传和秒传,记录一些过程中的细节。

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 // 获取原始的buffer

Blob

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 });
}