js對文件和二進制操作的一些方法匯總

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

FileReader

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

下面抄一段 MDN 的文檔

FileReader 對象允許Web應用程序異步讀取存儲在用戶計算機上的文件(或原始數(shù)據(jù)緩沖區(qū))的內容,使用 File 或 Blob 對象指定要讀取的文件或數(shù)據(jù).

其中File對象可以是來自用戶在一個 <input> 元素上選擇文件后返回的 FileList 對象,也可以來自拖放操作生成的 DataTransfer 對象,還可以是來自在一個 HTMLCanvasElement 上執(zhí)行 mozGetAsFile() 方法后返回結果.

由于是抄的,就不做詳細講解,重點是要知道你要讀的文件的編碼類型,然后調用對應的方法取讀就可以了.這里有原文

由于 FileReader 可以把文件讀取成各種格式,所以這里可以利用這個特性,進行編碼的轉換,如 ArrayBuffer, Blob對象 和 字符串, base64 之間的相互轉換/單向轉換, 部分類型只能單向轉換,是因為 FileReader 只接受 File 或 Blob 類型的數(shù)據(jù)(事實上 File 也 Blob 的一種),如果數(shù)據(jù)無法轉換成指定類型,就無法用 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 格式 或 純文本格式的數(shù)據(jù).

當然 FileReader 不僅僅能用在編碼轉換上,讀取各種文件才是它主要的能力,配合 <input type="file" >、DataTransfer脏榆、Blob 等,可以把任意格式的數(shù)據(jù)讀取到瀏覽器里面.

Blob 對象 (Binary Large Object)

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

Blob 對象表示一個不可變撑柔、原始數(shù)據(jù)的類文件對象.Blob 表示的不一定是JavaScript原生格式的數(shù)據(jù).File 接口基于Blob,繼承了 blob 的功能并將其擴展使其支持用戶系統(tǒng)上的文件.

Blob 對象有個同名構造函數(shù),該構造函數(shù)接收 2 個參數(shù),第一個參數(shù)必須是個 Array 類型的,哪怕你只有一項,也必須用 [ ] 包著,如 ['hello file-reader'],
第二個參數(shù)是個可選的,是個對象,有2個選項,type 和 endings,type 指定第一個參數(shù)的 MIME-Type, endings 指定第一個參數(shù)的數(shù)據(jù)格式,其可選值有 transparent(不變,默認) 和 native(隨系統(tǒng)轉換)

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

Blob.size 可以獲取對象中所包含數(shù)據(jù)的大小(字節(jié)), Blob.type 可以獲取對象所包含數(shù)據(jù)的MIME類型.如果類型未知,則該值為空字符串.

Blob.slice() 方法可以返回一個新的 Blob對象,包含了源 Blob對象中指定范圍內的數(shù)據(jù), 共接收3個參數(shù),前兩個參數(shù)和 Array.slice 的參數(shù)類似

參數(shù)1:開始索引,默認為0
參數(shù)2:截取結束索引(不包括當前值)
參數(shù)3:新Blob的MIME類型,默認為空字符串

const newBlob = blob.slice(0, 5, 'text/plain');

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

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

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

Arraybuffer, 類型數(shù)組對象, DataView

先說說 Arraybuffer 之所以要介紹它,是因為 FileReader 有個 readAsArrayBuffer() 方法,如果的被讀的文件是二進制數(shù)據(jù),那用這個方法去讀應該是最合適的,讀出來的數(shù)據(jù),就是一個 Arraybuffer 對象,老規(guī)矩,看看定義:

ArrayBuffer 對象用來表示通用的漩仙、固定長度的原始二進制數(shù)據(jù)緩沖區(qū).ArrayBuffer 不能直接操作,而是要通過類型數(shù)組對象或 DataView 對象來操作,它們會將緩沖區(qū)中的數(shù)據(jù)表示為特定的格式,并通過這些格式來讀寫緩沖區(qū)的內容.

Arraybuffer 也有個同名構造函數(shù),用于創(chuàng)建一個指定長度的,內容全部為 0 的 ArrayBuffer 對象,構造函數(shù)接收一個參數(shù),用來指定要創(chuàng)建的內容長度.如:

let ab = new ArrayBuffer(8); // 創(chuàng)建一個 8 字節(jié)的 ArrayBuffer

由于無法對 Arraybuffer 直接進行操作,所以我們需要借助其他對象來操作. 所有就有了 TypedArray(類型數(shù)組對象)和 DataView.

  1. TypedArray, TypedArray 是一類對象的統(tǒng)稱,事實上 JS 里面并沒有一個叫 TypedArray 的對象或構造函數(shù).所以你不能直接使用 TypedArray.以下是 9 個 TypedArray 對象/構造函數(shù)
Int8Array();
Uint8Array();
Uint8ClampedArray();
Int16Array();
Uint16Array();
Int32Array();
Uint32Array();
Float32Array();
Float64Array();

具體用法請參考 MDN: TypedArray

TypedArray 雖然不是真的數(shù)組,但是有幾乎跟數(shù)組一樣的 API,我們可以像操作數(shù)組一樣操作 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 數(shù)據(jù),不過這應該是瀏覽器為了方便開發(fā)者觀察數(shù)據(jù),而做的轉換,而不是 ArrayBuffer 真的擁有這些數(shù)據(jù),畢竟對象名稱看起來也不是那么正式(用 [[]] 包含)

arrayBuffer[0] = 1 給 ArrayBuffer 對象的某個下標賦值是不會報錯的,而且稍后你可以用同樣的路徑取出該值 console.log(arrayBuffer[0]) // 1, 但這并不代表你操作了 ArrayBuffer 的數(shù)據(jù),道理跟 給數(shù)組設置屬性 相同.

  1. DataView, DataView 提供了跟 TypedArray 類似的功能,與 TypedArray 不同的是 DataView 是一個真實存在的對象,通過提供各種方法來操作不通類型的數(shù)據(jù),直接看栗子吧.
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

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


// 16 位的 1

0000 0000 0000 0001

// 用 8 位的讀就變成

0000 0000  // 0

0000 0001  // 1

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


// 應該是256君躺? 我也不太會算這個

0000 0001 0000 0001

// 用 8 位的讀就變成

0000 0001  // 1

0000 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, 現(xiàn)在我們可以直接用內置的方法了

    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 https://github.com/pgriess/node-jspack js操作二進制文件, 我們的 psd 文件解析就用到這個包.

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

xml2js https://github.com/Leonidas-from-XIV/node-xml2js 把 xml 文件轉換成 json, 我們的 pptx 解析就是用它進行 pptx 的 xml 文件的轉換.

廣告時間

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

對本文有意見或者建議,請盡量在 github 上提 issue, 最近比較忙,比較不怎么逛社區(qū)

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市粒褒,隨后出現(xiàn)的幾起案子识颊,更是在濱河造成了極大的恐慌,老刑警劉巖奕坟,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祥款,死亡現(xiàn)場離奇詭異,居然都是意外死亡月杉,警方通過查閱死者的電腦和手機刃跛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來苛萎,“玉大人桨昙,你說我怎么就攤上這事‰缜福” “怎么了蛙酪?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長翘盖。 經常有香客問我桂塞,道長,這世上最難降的妖魔是什么最仑? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任藐俺,我火速辦了婚禮,結果婚禮上泥彤,老公的妹妹穿的比我還像新娘欲芹。我一直安慰自己,他們只是感情好吟吝,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布菱父。 她就那樣靜靜地躺著,像睡著了一般剑逃。 火紅的嫁衣襯著肌膚如雪浙宜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天蛹磺,我揣著相機與錄音粟瞬,去河邊找鬼。 笑死萤捆,一個胖子當著我的面吹牛裙品,可吹牛的內容都是我干的俗批。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼市怎,長吁一口氣:“原來是場噩夢啊……” “哼岁忘!你這毒婦竟也來了?” 一聲冷哼從身側響起区匠,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤干像,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后驰弄,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體麻汰,經...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年揩懒,在試婚紗的時候發(fā)現(xiàn)自己被綠了什乙。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挽封。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡已球,死狀恐怖,靈堂內的尸體忽然破棺而出辅愿,到底是詐尸還是另有隱情智亮,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布点待,位于F島的核電站阔蛉,受9級特大地震影響,放射性物質發(fā)生泄漏癞埠。R本人自食惡果不足惜状原,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望苗踪。 院中可真熱鬧颠区,春花似錦、人聲如沸通铲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽颅夺。三九已至朋截,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間吧黄,已是汗流浹背部服。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留拗慨,地道東北人廓八。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓厦酬,卻偏偏與公主長得像,于是被迫代替她去往敵國和親瘫想。 傳聞我的和親對象是個殘疾皇子仗阅,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內容