前言
在前端中處理文件時會經常遇到File、Blob、ArrayBuffer以及相關的處理方法或方式如FileReader、FormData等等這些名詞,對於這些常見而又不常見的名詞,我相信大多數人對它們都有一種熟悉的陌生人的感覺。究其原因,相關的東西接觸的不夠多,且每次都網上隨手拈來,不求甚解。今天,我們就稍微仔細一點,去做一個探究,弄清他們是誰,能做什么,又有什么區別,爭取下次再見既是“老朋友”。如果,你想更深入的了解相關知識點,可以參閱w3c和MDN的解釋,文后會附上相關的參考鏈接供參考。
內容
File
定義/概念
File即我們通常所說的文件,我們硬盤里存儲的音視頻、文檔等等都是文件。我們通常使用<input type="file">
來選取並讀取本地計算機中的文件,返回一個Filelist對象,此對象為一個類數組可迭代對象。File對象是特殊類型的Blob,所以順便也繼承了Blob特有的方法和屬性,同時又有自己獨特的屬性和方法。
MDN定義:文件(
File
)接口提供有關文件的信息,並允許網頁中的 JavaScript 訪問其內容。通常情況下,File
對象是來自用戶在一個<input>
元素上選擇文件后返回的FileList
對象,也可以是來自由拖放操作生成的DataTransfer
對象,或者來自HTMLCanvasElement
上的mozGetAsFile
() API。
用法/示例
File常用的屬性有:
File.name
只讀,返回當前File 對象所引用文件的名稱。
File.size
只讀,返回當前File 對象文件的大小。
File.type
只讀,返回文件的多用途互聯網郵件擴展類型(MIME Type)
更多屬性及方法信息可參考MDN,這里就不再詳細贅述。
FileList: <input type="file">
元素有一個files屬性,用來存儲用戶所選擇的文件,當用戶點擊選擇文件按鈕之后,便可以獲取到選擇的文件組成的FileList對象。
1 |
const fileList = document.getElementById('file').files; |
在這幾個當中,File應該是我們使用的頻率最高的一個,應該也是最熟悉的一個,所以過多的內容這里就不一一示例。這里引入一個很久之前遇到的一個相關的IE兼容性問題。
input[type=file]這個文件上傳原生按鈕不夠美觀,通常都是采取隱藏此原生的按鈕,使用另外一個自定義的按鈕,然后,通過點擊此按鈕間接觸發隱藏的原生按鈕,從而實現這一功能。但是,由於IE安全限制,我們間接通過clik()觸發的,在IE9某些版本就會報SCRIPT: 拒絕訪問
的錯誤。解決這個問題,要主動觸發上傳按鈕,此時借助label的for屬性,綁定到對應的input上即可解決此問題。
Blob
定義/概念
Blob是Binary Large Object
的縮寫,表示二進制大對象,它並不是前端的所特有對象,而是計算機界的通用術語,在一些數據庫中,例如,MYSQL中的BLOB類型就表示二進制數據的容器。MDN上對其的定義是:Blob
對象表示一個不可變、原始數據的類文件對象。可以通俗的說,Blob就是一只讀的二進制對象。從File的介紹我們已知File繼承自Blob,有許多相同的方法和屬性,因此可以像操作File對象一樣操作Blob對象。
用法/示例
Blob主要包含兩個屬性
Blob.size
:只讀,對象中所包含數據的大小(字節)Blob.type
:只讀,一個字符串,表明該Blob
對象所包含數據的 MIME 類型。如果類型未知,則該值為空字符串。(MIME類型參考)
創建一個Blob對象,需要調用Blob構造函數。
1 |
/** |
array 是一個由
ArrayBuffer
,ArrayBufferView
,Blob
,DOMString
等對象構成的Array
,或者其他類似對象的混合體,它將會被放進Blob
。DOMStrings
會被編碼為UTF-8。options 是一個可選的
BlobPropertyBag
字典,它可能會指定如下兩個屬性:
type
,默認值為""
,它代表了將會被放入到blob中的數組內容的MIME類型。endings
,默認值為"transparent"
,用於指定包含行結束符\n
的字符串如何被寫入。 它是以下兩個值中的一個:"native"
,代表行結束符會被更改為適合宿主操作系統文件系統的換行符,或者"transparent"
,代表會保持blob中保存的結束符不變
使用示例:
1 |
const data1 = "a"; |
以上blob5的size值打印為什么是15呢?原因是,當使用普通對象創建Blob對象時,相當於調用了普通對象的toString()
方法得到字符串數據,然后再創建Blob對象。所以,blob5保存的數據是"[object Object]"
,是15個字節(不包含最外層的引號)。
Blob目前有四個方法:
Blob.slice([start[, end[, contentType]]])
:返回一個新的 Blob
對象,包含了源 Blob
對象中指定范圍內的數據。(由於File繼承自Blob,可用此方法分割本地文件,實現分片上傳)
Blob.stream()
:返回一個能讀取blob內容的 ReadableStream
。
Blob.text()
:返回一個promise且包含blob所有內容的UTF-8格式的 USVString
。
Blob.arrayBuffer()
:返回一個promise且包含blob所有內容的二進制格式的 ArrayBuffer
ArrayBuffer
定義/概念
你從XHR、File API、Canvas等等各種地方,讀取了一大串字節流,如果用JS里的Array去存,又浪費,又低效。
於是為了配合這些新的API增強JS的二進制處理能力,就有了ArrayBuffer。
ArrayBuffer
簡單說就是一片內存,表示原始二進制數據緩沖區。但不能直接操作它,而是要通過類型數組對象TypedArray
或 DataView
(數據視圖)對象來操作它,它們會將緩沖區中的數據表示為特定的格式,並通過這些格式來讀寫緩沖區的內容。TypedArray
給ArrayBuffer
提供了一個“View
”,對它們進行下標讀寫。也可以使用DataView來讀寫ArrayBuffer
,DataView
能更自由的選擇字節序,不用考慮不同平台的字節序問題。
MDN將
ArrayBuffer
對象定義為用來表示通用的、固定長度的原始二進制數據緩沖區。它是一個字節數組,通常在其他語言中稱為“byte array”。
用法示例
由於ArrayBuffer
不能直接進行操作,故需要借助TypedArray
或者DataView
來進行讀寫。
1 |
// 生成一個可以16個字節的連續內存,每個字節的默認值是0 |
結語
區別/聯系
File和Blob
-
相同點: File和Blob都可以用來表示類文件對象,處理文件;
-
FIle: File可以看作一個承載文件的橋梁,將DOM接口和文件聯系起來,通過File這個橋梁,獲取計算及內的文件,從而對才能對文件做進一步處理。
-
Blob:File繼承自Blob,他們之間很方便進行轉換,Blob是File都原型對象。
-
聯系:File繼承自Blob,同時又有自己獨特的屬性和方法。從下面的打印可以看出,其實Blob對象就是File的原型對象,自然就擁有了Blob對象的方法和屬性。
1
<input type="file" id="myfiles" />
1
2
3
4
5
6
7const fileDOM = document.querySelector("#myfiles");
const fileChange = (e) => {
const files = fileDOM.files;
console.log(files[0].__proto__) // 輸出File
console.log(files[0].__proto__.__proto__) // 輸出Blob
}
fileDOM.onchange = fileChange;
Blob與ArrayBuffer
-
相同點:
Blob
和ArrayBuffer
都是二進制的容器。 -
ArrayBuffer:
ArrayBuffer
更底層,是一段純粹的內存上的二進制數據,我們可以對其任何一個字節進行單獨的修改,也可以根據我們的需要以我們指定的形式讀取指定范圍的數據。 -
Blob:
Blob
就是將二進制數據做了一個封裝,我們拿到的就是一個整體,可以看到它的整體屬性大小、類型;可以對其分割,但看不到它內部的細節 -
聯系:
Blob
可以接受一個ArrayBuffer
作為參數生成一個Blob
對象,此行為就相當於對ArrayBuffer
數據做一個封裝。 -
應用上的區別:由於
ArrayBuffer
和Blob
的特性,Blob
作為一個整體文件,適合用於文件傳輸;而只有需要關注細節(比如要修改某一段數據時),此時使用ArrayBuffer
比較好。
從以上我們的介紹以及聯系,我們可以得出如下的轉換函數
1 |
/** |
1 |
/** |
弄清了他們之間的關系,在以后的工作學習中,才能剛好的去使用這些對象,讓其用在最適用的地方。而不是每次都一頭霧水,熟悉並陌生着。對於和他們相關的FileReader、Base64、FormData,后續會更新相關內容,將其進行聯系起來,更好的理解他們。