【前端知乎系列】ArrayBuffer 和 Blob 對象

ArrayBufferAndBlob.png

本文首發在我的【個人博客】
更多豐富的前端學習資料,可以查看我的 Github: 《Leo-JavaScript》,內容涵蓋數據結構與算法HTTPHybrid面試題ReactAngularTypeScriptWebpack等等。
點個 Star 不迷路~

ArrayBuffer 對象與 Blob 對象大家或許不太陌生,常見於文件上傳操作處理(如處理圖片上傳預覽等問題)。

那麼本文將與大家深入介紹兩者。

一、ArrayBuffer 對象

ArrayBuffer 對象是 ES6 才納入正式 ECMAScript 規範,是 JavaScript 操作二進制數據的一個接口。ArrayBuffer 對象是以數組的語法處理二進制數據,也稱二進制數組。

介紹 ArrayBuffer 對象還需介紹 TypedArray 視圖和 DataView 視圖,本文不具體介紹,詳細可以查看阮一峯老師《ECMAScript 6 入門 ArrayBuffer》 章節。

1. 概念介紹

ArrayBuffer 對象代表儲存二進制數據的一段內存,它不能直接讀寫,只能通過視圖(TypedArray視圖和DataView視圖)來讀寫,視圖的作用是以指定格式解讀二進制數據。

關於 TypedArray 視圖和 DataView 視圖 ,可以查看阮一峯老師《ECMAScript 6 入門 ArrayBuffer》 章節的介紹。

2. 對象使用

瀏覽器原生提供 ArrayBuffer() 構造函數,用來生成實例。

參數:

  • 整數,表示二進制數據佔用的字節長度。

返回值:

  • 一個指定大小的 ArrayBuffer 對象,其內容被初始化爲 0。
const buffer = new ArrayBuffer(32);

上面代碼表示實例對象 buffer 佔用 32 個字節。

3. 實例屬性和方法

ArrayBuffer 對象有實例屬性 byteLength ,表示當前實例佔用的內存字節長度(單位字節),一單創建就不可變更(只讀)

const buffer = new ArrayBuffer(32);
buffer.byteLength; // 32

ArrayBuffer 對象有實例方法 slice(),用來複制一部分內存。

參數如下:

  • start,整數類型,表示開始複製的位置。默認從 0 開始。
  • end,整數類型,表示結束複製的位置(不包括結束的位置)。如果省略,則表示複製到結束。
const buffer = new ArrayBuffer(32);
const buffer2 = buffer.slice(0);

4. 兼容性

ArrayBuffer兼容性.png

圖片來自 MDN

二、Blob 對象

1. 概念介紹

Blob 全稱:Binary Large Object (二進制大型對象)。

Blob 對象表示一個二進制文件的數據內容,通常用來讀寫文件,比如一個圖片文件的內容就可以通過 Blob 對象讀寫。

ArrayBuffer 區別:

  • Blob 用於操作二進制文件
  • ArrayBuffer 用於操作內存

2. 對象使用

瀏覽器原生提供 Blob() 構造函數,用來生成實例。

Blob 的內容由參數數組中給出的值的串聯組成。

const leoBlob = new Blob(array [, options]);

參數:

  • array,必填,成員是字符串或二進制對象,表示新生成的Blob實例對象的內容;

成員可以是一個由 ArrayBuffer , ArrayBufferView , Blob , DOMString 等對象構成的 Array ,或者其他類似對象的混合體,它將會被放進 BlobDOMStrings會被編碼爲UTF-8

  • options,可選,是一個配置對象,這裏介紹常用的屬性 type,表示數據的 MIME 類型,默認空字符串;

options 目前可能有兩個屬性: typeendings

endings 用於指定包含行結束符 \n 的字符串如何被寫入,默認值 transparent。它只有這兩個值:native (代表行結束符會被更改爲適合宿主操作系統文件系統的換行符)和 transparent (代表會保持blob中保存的結束符不變)。

使用案例:

const leoHtmlFragment = ['<a id="a"><b id="b">hey leo!</b></a>']; // 一個包含 DOMString 的數組
const leoBlob = new Blob(leoHtmlFragment, {type : 'text/html'});   // 得到 blob

該代碼中,實例對象 leoBlob 包含的是字符串。生成實例時,指定數據類型爲 text/html

還可以使用 Blob 保存 JSON 數據:

const obj = { hello: 'leo' };
const blob = new Blob([ JSON.stringify(obj) ], {type : 'application/json'});

3. 實例屬性和方法

Blob 具有兩個實例屬性:

  • size:文件的大小,單位爲字節。
  • type:文件的 MIME 類型。如果類型無法確定,則返回空字符串。
const leoHtmlFragment = ['<a id="a"><b id="b">hey leo!</b></a>']; // 一個包含 DOMString 的數組
const leoBlob = new Blob(leoHtmlFragment, {type : 'text/html'});   // 得到 blob

leoBlob.size; // 38
leoBlob.type; // "text/html"

Blob 實例方法:

  • clice:方法用於創建一個包含源 Blob 的指定字節範圍內的數據的新 Blob 對象。
const newBlob = oldBlob.slice([start [, end [, contentType]]])

包含三個參數:

start,可選,起始的字節位置,默認 0;

end,可選,結束的字節位置,默認 size 屬性的值,不包含該位置;

contentType,可選,新實例的數據類型(默認爲空字符串);

4. 兼容性

Blob兼容性.png

圖片來自 MDN

5. 實際案例

5.1 獲取文件信息

文件選擇器 <input type="file"> 用來讓用戶選取文件。出於安全考慮,瀏覽器不允許腳本自行設置這個控件的 value 屬性,即文件必須是用戶手動選取的,不能是腳本指定的。一旦用戶選好了文件,腳本就可以讀取這個文件。

文件選擇器返回一個 FileList 對象,該對象是個類數組對象,每個成員都是一個 File 實例對象。File 實例對象是一個特殊的 Blob 實例,增加了 namelastModifiedDate 屬性。

也包括拖放 APIdataTransfer.files 返回的也是一個 FileList 對象,成員也是 File 實例對象。

// HTML 代碼如下
// <input type="file" accept="image/*" multiple οnchange="fileinfo(this.files)"/>

function fileinfo(files) {
  for (let i = 0; i < files.length; i++) {
    let f = files[i];
    console.log(
      f.name, // 文件名,不含路徑
      f.size, // 文件大小,Blob 實例屬性
      f.type, // 文件類型,Blob 實例屬性
      f.lastModifiedDate // 文件的最後修改時間
    );
  }
}

5.2 下載文件

在 AJAX 請求中,指定 responseType 屬性爲 blob ,皆可以下下載一個 Blob 對象。

function getBlob(url, callback) {
  const xhr = new XMLHttpRequest();
  xhr.open('GET', url);
  xhr.responseType = 'blob';
  xhr.onload = function () {
    callback(xhr.response);
  }
  xhr.send(null);
}

然後,xhr.response 拿到的就是一個 Blob 對象。

5.3 生成 URL

瀏覽器允許使用 URL.createObjectURL() 方法,針對 Blob 對象生成一個臨時 URL,以便於某些 API 使用。

如作爲圖片預覽的 URL。

這個 URL 以 blob:// 開頭,表明對應一個 Blob 對象,協議頭後面是一個識別符,用來唯一對應內存裏面的 Blob 對象。這一點與 data://URL(URL 包含實際數據)和 file://URL(本地文件系統裏面的文件)都不一樣。

const droptarget = document.getElementById('droptarget');

droptarget.ondrop = function (e) {
  const files = e.dataTransfer.files;
  for (let i = 0; i < files.length; i++) {
    let type = files[i].type;
    if (type.substring(0,6) !== 'image/')
      continue;
    let img = document.createElement('img');
    img.src = URL.createObjectURL(files[i]);
    img.onload = function () {
      this.width = 100;
      document.body.appendChild(this);
      URL.revokeObjectURL(this.src);
    }
  }
}

代碼中,通過爲拖放的圖片文件生成一個 URL,作爲預覽的縮略圖。

瀏覽器處理 Blob URL 就跟普通的 URL 一樣,如果 Blob 對象不存在,返回404狀態碼;如果跨域請求,返回403狀態碼。Blob URL 只對 GET 請求有效,如果請求成功,返回200狀態碼。由於 Blob URL 就是普通 URL,因此可以下載。

5.4 讀取文件

取得 Blob 對象以後,可以通過 FileReader 對象,讀取 Blob 對象的內容,即文件內容。

FileReader 對象提供四個方法。將 Blob 對象作爲參數傳入,然後以指定的格式返回。

  • FileReader.readAsText():返回文本,需要指定文本編碼,默認爲 UTF-8。
  • FileReader.readAsArrayBuffer():返回 ArrayBuffer 對象。
  • FileReader.readAsDataURL():返回 Data URL。
  • FileReader.readAsBinaryString():返回原始的二進制字符串。

下面是 FileReader.readAsText() 方法的例子,用來讀取文本文件:

// HTML 代碼如下
// <input type='file' οnchange='readfile(this.files[0])'></input>
// <pre id='output'></pre>
function readfile(f) {
  let reader = new FileReader();
  reader.readAsText(f);
  reader.onload = function () {
    let text = reader.result;
    let out = document.getElementById('output');
    out.innerHTML = '';
    out.appendChild(document.createTextNode(text));
  }
  reader.onerror = function(e) {
    console.log('Error', e);
  };
}

下面是 FileReader.readAsArrayBuffer() 方法的例子,用於讀取二進制文件:

// HTML 代碼如下
// <input type="file" οnchange="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);
  };
}

三、參考資料

  1. 《ArrayBuffer 對象,Blob 對象》
  2. 《ECMAScript 6 入門 ArrayBuffer》

四、關於我

本文首發在 pingan8787個人博客,如需轉載請保留個人介紹。
Author 王平安
E-mail [email protected]
博 客 www.pingan8787.com
微 信 pingan8787
每日文章推薦 https://github.com/pingan8787...
ES小冊 js.pingan8787.com

bg

發佈了30 篇原創文章 · 獲贊 0 · 訪問量 4969
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章