本文首發在 個人博客
更多豐富的前端學習資料,可以查看我的 Github: 《Leo-JavaScript》,內容涵蓋數據結構與算法、HTTP、Hybrid、面試題、React、Angular、TypeScript和Webpack等等。點個 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. 兼容性
圖片來自 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
,或者其他類似對象的混合體,它將會被放進 Blob
。DOMStrings
會被編碼為UTF-8
。
-
options
,可選,是一個配置對象,這里介紹常用的屬性type
,表示數據的 MIME 類型,默認空字符串;
options
目前可能有兩個屬性:type
和 endings
。
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. 兼容性
圖片來自 MDN
5. 實際案例
5.1 獲取文件信息
文件選擇器 <input type="file">
用來讓用戶選取文件。出於安全考慮,瀏覽器不允許腳本自行設置這個控件的 value
屬性,即文件必須是用戶手動選取的,不能是腳本指定的。一旦用戶選好了文件,腳本就可以讀取這個文件。
文件選擇器返回一個 FileList
對象,該對象是個類數組對象,每個成員都是一個 File
實例對象。File
實例對象是一個特殊的 Blob
實例,增加了 name
和 lastModifiedDate
屬性。
也包括拖放 API 的 dataTransfer.files
返回的也是一個 FileList
對象,成員也是 File
實例對象。
// HTML 代碼如下 // <input type="file" accept="image/*" multiple onchange="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' onchange='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" 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); }; }
三、參考資料
1. 《ArrayBuffer 對象,Blob 對象》
https://wangdoc.com/javascript/bom/arraybuffer.html
2. 《ECMAScript 6 入門 ArrayBuffer》
https://es6.ruanyifeng.com/#docs/arraybuffe
