關於 ArrayBuffer

Grewer發表於2021-12-01

ArrayBuffer 是什麼

ArrayBuffer 物件用來表示通用的、固定長度的原始二進位制資料緩衝區。

它是一個位元組陣列,通常在其他語言中稱為“byte array”。

你不能直接操作 ArrayBuffer 的內容,而是要通過型別陣列物件或 DataView 物件來操作,它們會將緩衝區中的資料表示為特定的格式,並通過這些格式來讀寫緩衝區的內容。

簡單用法

const buffer = new ArrayBuffer(8);

console.log(buffer.byteLength); // 8

length 大於 Number.MAX_SAFE_INTEGER(>= 2 53)或為負數,則丟擲一個 RangeError 異常。**

作用

從XHR、File API、Canvas, WebGL 等等各種地方,讀取了一大串位元組流,如果用JS裡的Array去存,又浪費,又低效

於是為了配合這些新的API增強JS的二進位制處理能力,就有了ArrayBuffer

建立 ArrayBuffer 的時候,就相當於申請了一塊記憶體, 不能(也不方便)直接用它

所以也就有了 TypedArray, 比如 Uint32Array,Int16Array , Int8Array, Float32Array 等等等等

這些就是用來操作 TypedArray 的具體實現

TypeArray

一個型別化陣列(TypedArray)物件描述了一個底層的二進位制資料緩衝區(binary data buffer)的一個類陣列檢視(view)。事實上,沒有名為 TypedArray 的全域性屬性,也沒有一個名為 TypedArray 的建構函式。相反,有許多不同的全域性屬性,它們的值是特定元素型別的型別化陣列建構函式,如下所示
// 下面程式碼是語法格式,不能直接執行,
// TypedArray 關鍵字需要替換為底部列出的建構函式。
new TypedArray(); // ES2017中新增
new TypedArray(length);
new TypedArray(typedArray);
new TypedArray(object);
new TypedArray(buffer [, byteOffset [, length]]);
// TypedArray 指的是以下的其中之一:

Int8Array();
Uint8Array();
Uint8ClampedArray();
Int16Array();
Uint16Array();
Int32Array();
Uint32Array();
Float32Array();
Float64Array();
  • Unit8Array 指的是,把 ArrayBuffer 的每個 byte(8-bit) 當作一個單獨的無符號整型數字 (0 - 255)
  • Unit16Array 表示為使用 16 bits (2 bytes) 表示一個無符號整型 (0 ~ 2^16-1) 的數的陣列
  • Int8Array 表示使用 8 bits 表示一個有符號整型 (-128 ~ 127)
  • Float32Array 表示使用 32 bits 表示一個浮點數
  • Unit7ClampedArray 在 0 ~ 255 範圍內和 Unit8Array 是一樣的,對超出範圍的處理有所不同,和影像處理相關(一般畫素範圍也是 0 ~ 255)

使用場景:

檔案的轉換:

arrayBuffer 可以轉換為 Blob 物件:

 const blob = new Blob([arrayBuffer],{type:"xxx/xxx"});

Blob 轉換為 arrayBuffer:


  const fileReader:FileReader = new FileReader();

  fileReader.addEventListener("load",(event)=>{
    const arrayBuffer = event.target.result;
    //...
  })

  fileReader.readAsArrayBuffer(file);

或者:

  const arrayBuffer = await file.arrayBuffer();

從 ajax 獲取:

  var xhr = new XMLHttpRequest();
  xhr.open('GET', 'xxxxx');

  xhr.responseType = 'arraybuffer';

  xhr.onload = function(e) {
    if (this.status == 200) {
      const arrayBuffer = xhr.response;
    }
  };

  xhr.send();

例子 1

音訊的某一種播放方法

  const audioContext = new AudioContext();

  const buffer = await file.arrayBuffer();

  const auidoBuffer = await audioContext.decodeAudioData(buffer);

  const source = audioContext.createBufferSource();

  source.buffer = auidoBuffer;

  source.connect(audioContext.destination);
  source.start();

例子 2

讀取二進位制檔案

// HTML 程式碼如下
// <input type="file" onchange="typefile(this.files[0])"></input>
function typefile(file) {
  // 檔案開頭的四個位元組,生成一個 Blob 物件
  let slice = file.slice(0, 4);
  let reader = new FileReader();
  // 讀取這四個位元組
  reader.readAsArrayBuffer(slice);
  reader.onload = function (e) {
    let buffer = reader.result;
    // 將這四個位元組的內容,視作一個32位整數
    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;
    }
    console.log(file.name, file.verified_type);
  };
}

總結

這裡還有很多東西沒講, 比如 SharedArrayBuffers, 大小端問題等等, 想要深入的話可以自行了解

在前端使用到 buffer 的場景確實非常少見, 但涉及到比較底層或者偏門一點點的時候就會看到他了, 這個時候也要求我們要了解他, 比如檔案, canvas, WebGL, WASM, EXCEL 處理

引用