js對檔案和二進位制操作的一些方法彙總

noahlam發表於2019-01-24

最近接手了一個專案,接觸到一些對檔案操作的業務.所以在這邊整理一下日常用到的處理方式,當學習筆記吧,有不對的地方,歡迎指正哈

FileReader

首先我們來看一下 FileReader 這個萬能的物件, 就如同它的名字一樣,就是個檔案讀取器,之所以說它是個萬能的物件是因為它可以讀取任意格式的內容,最近我嘗試過用 FileReader 讀取過 psd, ppt, 各種圖片等等.雖然很多情況下,它讀出來的是我們完全看不懂的東西.不過通過一定的轉換,理論上我們可以在瀏覽器裡面開啟任何檔案型別.

下面抄一段 MDN 的文件

FileReader 物件允許Web應用程式非同步讀取儲存在使用者計算機上的檔案(或原始資料緩衝區)的內容,使用 File 或 Blob 物件指定要讀取的檔案或資料.

其中File物件可以是來自使用者在一個 <
input>
元素上選擇檔案後返回的 FileList 物件,也可以來自拖放操作生成的 DataTransfer 物件,還可以是來自在一個 HTMLCanvasElement 上執行 mozGetAsFile() 方法後返回結果.

由於是抄的,就不做詳細講解,重點是要知道你要讀的檔案的編碼型別,然後呼叫對應的方法取讀就可以了.這裡有原文

由於 FileReader 可以把檔案讀取成各種格式,所以這裡可以利用這個特性,進行編碼的轉換,如 ArrayBuffer, Blob物件 和 字串, base64 之間的相互轉換/單向轉換, 部分型別只能單向轉換,是因為 FileReader 只接受 File 或 Blob 型別的資料(事實上 File 也 Blob 的一種),如果資料無法轉換成指定型別,就無法用 FileReader 轉換.

 const filereader = new FileReader();
const blob = new Blob(['hello file-reader'], {
type: 'text/plain'
});
filereader.onload = e =>
{
console.log(e.target.result);
// 輸出 data:text/plain;
base64,aGVsbG8gZmlsZS1yZWFkZXI=

} filereader.readAsDataURL(blob);
複製程式碼

網上傳的用 Uint16Array 進行 String 和 ArrayBufer 互轉的其實有編碼長度的問題,我親身體驗過,用 FileReader 就可以避開這個問題.可以呼叫它的 readAsArrayBuffer()readAsText() 方法,把指定物件讀取成 ArrayBuffer 格式 或 純文字格式的資料.

當然 FileReader 不僅僅能用在編碼轉換上,讀取各種檔案才是它主要的能力,配合 <
input type=”file” >
、DataTransfer、Blob 等,可以把任意格式的資料讀取到瀏覽器裡面.

Blob 物件 (Binary Large Object)

上面多次提到 Blob 物件,私以為 Blob 也是個非常強大的物件,所以這裡我覺得非常有必要介紹一下,先看看 MDN 怎麼說的

Blob 物件表示一個不可變、原始資料的類檔案物件.Blob 表示的不一定是JavaScript原生格式的資料.File 介面基於Blob,繼承了 blob 的功能並將其擴充套件使其支援使用者系統上的檔案.

Blob 物件有個同名建構函式,該建構函式接收 2 個引數,第一個引數必須是個 Array 型別的,哪怕你只有一項,也必須用 [ ] 包著,如 ['hello file-reader'],第二個引數是個可選的,是個物件,有2個選項,type 和 endings,type 指定第一個引數的 MIME-Type, endings 指定第一個引數的資料格式,其可選值有 transparent(不變,預設) 和 native(隨系統轉換)

const blob = new Blob(['hello file-reader'], { 
type: 'text/plain'
});
複製程式碼

Blob.size 可以獲取物件中所包含資料的大小(位元組), Blob.type 可以獲取物件所包含資料的MIME型別.如果型別未知,則該值為空字串.

Blob.slice() 方法可以返回一個新的 Blob物件,包含了源 Blob物件中指定範圍內的資料, 共接收3個引數,前兩個引數和 Array.slice 的引數類似

引數1:開始索引,預設為0引數2:擷取結束索引(不包括當前值)引數3:新Blob的MIME型別,預設為空字串

const newBlob = blob.slice(0, 5, 'text/plain');
複製程式碼

大檔案分段上傳就靠它了,配合 Blob.size 食用,口感更佳哈

通過 URL.createObjectURL(Blob物件), 可以把 Blob物件 轉換成一個連結地址,該地址可以直接用在某些 DOM 的 src 或者 href 上, 從而實現前端下載或圖片顯示.一個比較w神奇的用法是 阮老師的 web worker 教程 裡的「同頁面的 Web Worker」, 這個再配合動態插入 DOM, 是不是就可以繞開 webworker 的同源策略?

上面提到 FileReader 只能接受 Blob 格式的資料(其他格式其實也是Blob的子集), 其實 Blob 也只能通過 FileReader 讀取.簡直就是泡麵跟火腿腸,最佳搭檔啊哈

Arraybuffer, 型別陣列物件, DataView

先說說 Arraybuffer 之所以要介紹它,是因為 FileReader 有個 readAsArrayBuffer() 方法,如果的被讀的檔案是二進位制資料,那用這個方法去讀應該是最合適的,讀出來的資料,就是一個 Arraybuffer 物件,老規矩,看看定義:

ArrayBuffer 物件用來表示通用的、固定長度的原始二進位制資料緩衝區.ArrayBuffer 不能直接操作,而是要通過型別陣列物件或 DataView 物件來操作,它們會將緩衝區中的資料表示為特定的格式,並通過這些格式來讀寫緩衝區的內容.

Arraybuffer 也有個同名建構函式,用於建立一個指定長度的,內容全部為 0 的 ArrayBuffer 物件,建構函式接收一個引數,用來指定要建立的內容長度.如:

let ab = new ArrayBuffer(8);
// 建立一個 8 位元組的 ArrayBuffer複製程式碼

由於無法對 Arraybuffer 直接進行操作,所以我們需要藉助其他物件來操作. 所有就有了 TypedArray(型別陣列物件)和 DataView.

  1. TypedArray, TypedArray 是一類物件的統稱,事實上 JS 裡面並沒有一個叫 TypedArray 的物件或建構函式.所以你不能直接使用 TypedArray.以下是 9 個 TypedArray 物件/建構函式
Int8Array();
Uint8Array();
Uint8ClampedArray();
Int16Array();
Uint16Array();
Int32Array();
Uint32Array();
Float32Array();
Float64Array();
複製程式碼

具體用法請參考 MDN: TypedArray

TypedArray 雖然不是真的陣列,但是有幾乎跟陣列一樣的 API,我們可以像運算元組一樣操作 TypedArray ,所以有了 TypedArray 我們就可以把 ArrayBuffer 轉換成 TypedArray,然後在進行讀寫操作,達到操作二進位制的目的,下面是個例子

    let arrayBuffer = new ArrayBuffer(8);
console.log(arrayBuffer[0]);
// undefined let uint8Array = new Uint8Array(arrayBuffer);
console.log(uint8Array);
// [0, 0, 0, 0, 0, 0, 0, 0] uint8Array[0] = 1;
console.log(uint8Array[0]);
// 1 console.log(uint8Array);
// [1, 0, 0, 0, 0, 0, 0, 0]複製程式碼

可以看出,用 arrayBuffer[0] 的方式直接獲取 ArrayBuffer 物件的內容是獲取不到的,而 TypedArray 可以.

直接 console.log(arrayBuffer) 在控制檯是可以看到 [[Int8Array]] [[Int16Array]] [[Int32Array]] [[Uint8Array]] 4 種 TypedArray 資料,不過這應該是瀏覽器為了方便開發者觀察資料,而做的轉換,而不是 ArrayBuffer 真的擁有這些資料,畢竟物件名稱看起來也不是那麼正式(用 [[]] 包含)

arrayBuffer[0] = 1 給 ArrayBuffer 物件的某個下標賦值是不會報錯的,而且稍後你可以用同樣的路徑取出該值 console.log(arrayBuffer[0]) // 1, 但這並不代表你操作了 ArrayBuffer 的資料,道理跟 給陣列設定屬性 相同.

  1. DataView, DataView 提供了跟 TypedArray 類似的功能,與 TypedArray 不同的是 DataView 是一個真實存在的物件,通過提供各種方法來操作不通型別的資料,直接看栗子吧.
let arrayBuffer = new ArrayBuffer(8);
let dataView = new DataView(arrayBuffer);
console.log(dataView.getUint8(1));
// 0 dataView.setUint8(1, 2);
console.log(dataView.getUint8(1));
// 2 console.log(dataView.getUint16(1));
// 512 dataView.setUint16(1, 255);
console.log(dataView.getUint16(1));
// 255 console.log(dataView.getUint8(1));
// 0複製程式碼

就像你看到的,我們可以在同一個資料上面,呼叫不同的方法,讀取/寫入 不同型別(長度)的資料,但是大部分情況下,這麼做會很難得到我們預期的效果.就像上面的輸出,看起來好像不是那麼的正常,這是因為 一個 16 位的二進位制,用 8 位的格式來讀,剛好可以讀成 2 個 8 位的二進位制.舉個例子

// 16 位的 10000 0000 0000 0001// 用 8 位的讀就變成0000 0000  // 00000 0001  // 1複製程式碼

因為前面 8 位剛好都是 0 , 所以結果看起來除了多個 0 似乎沒啥區別? 當數字比較大的時候

// 應該是256? 我也不太會算這個0000 0001 0000 0001// 用 8 位的讀就變成0000 0001  // 10000 0001  // 1複製程式碼

扯遠了,我們回頭看看 DataView 提供了哪些方法

// 讀DataView.prototype.getInt8()DataView.prototype.getUint8()DataView.prototype.getInt16()DataView.prototype.getUint16()DataView.prototype.getInt32()DataView.prototype.getUint32()DataView.prototype.getFloat32()DataView.prototype.getFloat64()// 寫DataView.prototype.setInt8()DataView.prototype.setUint8()DataView.prototype.setInt16()DataView.prototype.setUint16()DataView.prototype.setInt32()DataView.prototype.setUint32()DataView.prototype.setFloat32()DataView.prototype.setFloat64()複製程式碼

跟 TypedArray 比 少了一個 Uint8ClampedArray() 具體看 MDN: DataView

atob 和 btoa

base64 這個利器,相信前端的你不會陌生吧,最常用的操作可能就是圖片轉 base64 了吧? 在之前 要在字串跟 base64 直接互轉,我們可能需要去網上拷一個別人的方法,而且大部分情況下,你沒有時間去驗證這個方法是不是真的可靠,有沒有 bug, 現在我們可以直接用內建的方法了

    let str = 'I am a string';
let a = btoa(str);
// a = 'SSBhbSBhIHN0cmluZw==' let b = atob(a);
// b = 'I am a string'複製程式碼

沒錯,就是這麼簡單,而且大部分瀏覽器都支援 除了 IE9-, 具體可以參考 CanIUse: atob

btoa 方法不支援中文和特殊字元,所以保險起見,在轉換之前還是 encodeURIComponent 一下吧, 當然別忘了在 atob 後,再 decodeURIComponent 回來。

jspack zipjs xml2js

最後再安利 3 個包

jspack github.com/pgriess/nod… js操作二進位制檔案, 我們的 psd 檔案解析就用到這個包.

jszip github.com/Stuk/jszip js操作壓縮檔案, 我們的 pptx 的壓縮比解析成 xml 都靠它.

xml2js github.com/Leonidas-fr… 把 xml 檔案轉換成 json, 我們的 pptx 解析就是用它進行 pptx 的 xml 檔案的轉換.

廣告時間

我們40人的前端團隊常年招兵買馬中,在廈門的和想來廈門的童鞋們,不要吝惜你的簡歷,使勁砸過來 郵箱:atob('bnVveWFAZ2FvZGluZy5jb20='), 期待你一起來稿

對本文有意見或者建議,請儘量在 github 上提 issue, 最近比較忙,比較不怎麼逛社群

來源:https://juejin.im/post/5c4913fe6fb9a049d9758deb

相關文章