Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blob 与 ArrayBuffer #91

Open
pfan123 opened this issue Apr 19, 2021 · 0 comments
Open

Blob 与 ArrayBuffer #91

pfan123 opened this issue Apr 19, 2021 · 0 comments

Comments

@pfan123
Copy link
Owner

pfan123 commented Apr 19, 2021

Blob 与 ArrayBuffer

早期 JS 一直没有比较好的可以直接处理二进制的方法。而 Blob 的存在,允许我们可以通过JS直接操作二进制数据。ArrayBuffer 是 Blob 数据格式的组成部分。

Blob

Blob(Binary Large Object) 表示二进制类型的大对象。Blob 的概念在一些数据库中有使用到,如 MYSQL 中的 BLOB 类型就表示二进制数据的容器。在 JavaScript 中 Blob 类型的对象表示不可变的类似文件对象的原始数据,会存储在内存中。

File 接口基于 Blob ,继承了 Blob 的功能并将其扩展使其支持用户系统上的文件。

Blob 由一个可选的字符串 type (通常是 MIME 类型)和 blobParts 组成:

img

构造 Blob

const blob = new Blob(['我是Blob'],{type: 'text/html'}); 

Blob 属性

Blob 对象含有两个属性:size 和 type。其中 size 属性用于表示数据的大小(以字节为 单位), type 是 MIME 类型的字符串。

blob.size   //Blob大小(以字节为单位)

blob.type   //Blob的MIME类型,如果是未知,则是“ ”(空字符串)          

Blob URL/Object URL

Blob URL/Object URL 是一种伪协议,允许 Blob 和 File 对象用作图像,下载二进制数据链接等URL源。在浏览器中,我们使用 URL.createObjectURL 方法来创建 Blob URL,该方法接收一个 Blob 对象,并为其创建一个唯一的 URL,其形式为 blob:<origin>/<uuid> ,如下:

blob:https://example.org/40a5fb5a-d56d-4a33-b4e2-0acf6a8e5f641

浏览器内部为每个通过 URL.createObjectURL 生成的 URL 存储了一个 URL → Blob 映射。生成的 URL 仅在当前文档打开的状态下才有效。

const blobURL = URL.createObjectURL(blob);

Blob URL 实际上会存在有副作用。存储的 URL → Blob 映射,会导致驻留在内存中的 Blob 对象,无法被浏览器释放。在文档卸载时会映射自动清除,Blob 对象也会随之被释放。但如果应用程序寿命很⻓,就会出现长期占用内存的情况。

针对这个问题,可以调用 URL.revokeObjectURL(url) 方法,从内部映射中删除引用,从而允许删除 Blob(如果没有其他引用),并释放内存。

window.URL.revokeObjectURL(blobURL);          

Blob 常见使用场景

  • 大文件切片上传,下载
function upload(blobOrFile) {

  const xhr = new XMLHttpRequest();

  xhr.open('POST', '/server', true);

  xhr.onload = function(e) { ... };

  xhr.send(blobOrFile);

}



document.querySelector('input[type="file"]').addEventListener('change', function(e) {

  const blob = this.files[0];



  const BYTES_PER_CHUNK = 1024 * 1024; // 1MB chunk sizes.

  const SIZE = blob.size;



  let start = 0;

  let end = BYTES_PER_CHUNK;



  while(start < SIZE) {

    upload(blob.slice(start, end));



    start = end;

    end = start + BYTES_PER_CHUNK;

  }

}, false)
  • 隐藏视频源路径
const video = document.getElementById('video');

const obj_url = window.URL.createObjectURL(blob);  // ArrayBuffer Blob

video.src = obj_url;

video.play()

window.URL.revokeObjectURL(obj_url);
  • 图片跨域请求,处理跨域问题,规避 canvas "被污染"的画布问题, 参考 createjs ImageLoader.js

在"被污染"的画布中调用以下方法将会抛出安全错误:

  • 的上下文上调用getImageData()

  • 上调用 toBlob()

  • 上调用 toDataURL()

  • 结合 canvas 绘图,使用 createObjectURL(blob) 输出页面,实现移动端长按保存、转发,示例

  • Web Worker 串行加载优化

使用 Blob、URL.createObjectURL 优化后,减少一次请求:

service worker 脚本的 URL, 不支持 Blob/String URL,Create service worker from Blob/String URL

const workerContent = `self.addEventListener('message', function (evt) {

    const num1 = evt.data.num1;

    const num2 = evt.data.num2;

    const result = num1 + num2;

    self.postMessage({result: result});

}, false)`;



const workerBlob = new Blob([workerContent], { type:'text/javascript' });

const workerUrl = window.URL.createObjectURL(workerBlob);

const worker = new Worker(workerUrl);

window.URL.revokeObjectURL(workerUrl);



worker.addEventListener('message', function(evt) {

    console.log(`[main] result is: ${evt.data.result}.`);

}, false);



worker.postMessage({num1: 20, num2: 10});

ArrayBuffer

ES2015 推出之后,JavaScript 开始支持在原始二进制缓冲区中读取和写入数据,这个缓冲区被称为 ArrayBuffer 。

ArrayBuffer 对象用来表示通用的、固定长度的原始二进制数据缓冲区。它是一个字节数组,通常在其他语言中称为“byte array”。

我们不能直接操作 ArrayBuffer 的内容,而是要通过 TypedArray 对象或 DataView 对象来操作,它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区的内容。

ArrayBuffer 目前似乎应用的场景不多,但了解它,可以让我们理解 JavaScript 如何直接操作字节模块。

可尝试利用 ArrayBuffer 与 WebAssembly 进行更高效率的交互

TypedArray 操作 ArrayBuffer

TypedArray 允许程序以统一的类型数组的形式访问缓冲区,例如 Int8Array、Int16Array 或 Float32Array 。

const buffer = new ArrayBuffer(32);
const array = new Int16Array(buffer);

for (let i = 0; i < array.length; i++) {
  array[i] = i * i;
}

DataView 操作 ArrayBuffer

DataView 允许更细粒度的数据访问。 我们可以通过为每种值类型提供专门的 getter 和 setter 来选择从缓冲区读取和写入的值的类型,这使得 DateView 可用于序列化数据结构。

const buffer = new ArrayBuffer(32);

const view = new DataView(buffer);



const person = { age: 42, height: 1.76 };



view.setUint8(0, person.age);

view.setFloat64(1, person.height);



console.log(view.getUint8(0)); // 输出: 42

console.log(view.getFloat64(1)); // 输出: 1.76

Buffer

Buffer 是 Node.JS 中用于操作 ArrayBuffer 的视图,是 TypedArray 的一种。

Buffer 类是 JavaScript 的 Uint8Array 类的子类,且继承时带上了涵盖额外用例的方法。 只要支持 Buffer 的地方,Node.js API 都可以接受普通的 Uint8Array

创建一个Buffer 对象时,JavaScript会根据请求内存的大小,分成两种方式来分配内存。

  • **若其大小小于内存池(memory pool)的一半且不为0的时候,**它会用整个内存池去分配所需要的内存
  • 否则将创建一个与所需大小一致的 ArrayBuffer 对象

Blob 对象与 ArrayBuffer 对象对比

Blob 对象与 ArrayBuffer 对象拥有各自的特点,它们之间的区别如下:

  • Blob 对象是不可变的,而 ArrayBuffer 是可以通过 TypedArrays 或 DataView 来操作。

  • ArrayBuffer 是存在 V8 内存中的,可以直接操作。而 Blob 可以位于磁盘、高速缓存内存和其他不可用的位置。

  • Blob 与 ArrayBuffer 对象之间是可以相互转化的:

    • 使用 FileReader 的 readAsArrayBuffer() 方法,可以把 Blob 对象转换为 ArrayBuffer 对 象;
document.querySelector('input[type="file"]').addEventListener('change', function(e) {

  const blob = this.files[0];

  const fileType = blob.slice(0, 4);

  const reader = new FileReader();

  reader.readAsArrayBuffer(fileType);

  reader.onload = function (e) {

    let buffer = reader.result;

    let view = new DataView(buffer);

    let magic = view.getUint32(0, false);

    switch(magic) {

      case 0x89504E47: file.verified_type = 'image/png'; break;

      case 0x47494638: file.verified_type = 'image/gif'; break;

      case 0x25504446: file.verified_type = 'application/pdf'; break;

      case 0x504b0304: file.verified_type = 'application/zip'; break;

    }

  };

}, false)
  • 使用 Blob 构造函数,如 new Blob([new Uint8Array(data]),可以把 ArrayBuffer 对 象转换为 Blob 对象。

  • AJAX 场景下,我们也可能会用到 Blob 或 ArrayBuffer 对象。

function GET(url, callback) {

let xhr = new XMLHttpRequest();

xhr.open('GET', url, true);

xhr.responseType = 'arraybuffer'; // or xhr.responseType = "blob"; xhr.send();

xhr.onload = function(e) { if (xhr.status != 200) {

alert("Unexpected status code " + xhr.status + " for " + url);

      return false;

    }

callback(new Uint8Array(xhr.response)); // or new Blob([xhr.response]); };

}

Other Resources

ArrayBuffer 和 Buffer 有何区别?

Node.js 中的缓冲区(Buffer)究竟是什么?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant