JavaScript 檔案物件詳解

Mr-dang發表於2019-03-04

在瀏覽器中操作檔案,多數情況下用到的是 File 物件,從 <input type=`file` /> 元素獲取,進而繼續操作(例如將選擇的圖片展示在頁面上,用ajax將檔案上傳至伺服器等)。這裡介紹在瀏覽器中操作檔案的相關API.

File 物件繼承自 Blob 物件,先看看 Blob 物件。

1. Blob 物件

Blob 物件表示一個不可變、原始資料的類檔案物件。Blob 表示的不一定是JavaScript原生格式的資料。

Blob建構函式 Blob(array[, options])

  • array 是一個由ArrayBuffer, ArrayBufferView, Blob, string 等物件構成的 Array ,或者其他類似物件的混合體,它將會被放進 Blob。string會被編碼為UTF-8。
  • options 是一個可選的物件,它可能會指定如下兩個屬性:
    • type,預設值為 “”,它代表了將會被放入到blob中的陣列內容的MIME型別
    • endings,預設值為”transparent”,用於指定包含行結束符
      的字串如何被寫入。 它是以下兩個值中的一個: “native”,代表行結束符會被更改為適合宿主作業系統檔案系統的換行符,或者 “transparent”,代表會保持blob中儲存的結束符不變。

示例:

var content1 = [`This is my firt trip to an island`];
var blob1 = new Blob(content, {type: `text/plain`});
var content2 = {name: `Alice`, age: 23};
var blob2 = new Blob([JSON.stringify(content2, null, 2)], {type: `application/json`});
複製程式碼

Blob例項屬性

屬性名稱 讀/寫 描述
size 只讀 Blob 物件中所包含資料的大小(位元組)。
type 只讀 一個字串,表明該Blob物件所包含資料的MIME型別。如果型別未知,則該值為空字串。例如 “image/png”.

示例:

var content = [`<div id="box"><p class="pra">a paragraph</p></div>`];
var blob = new Blob(content, {type: `text/html`});
console.log(blob.size); // 50
console.log(blob.type); // text/html
複製程式碼

Blob例項方法

  • slice([start[, end[, contentType]]])

slice 方法接收三個可選引數,startend 都是數值,表示擷取的範圍,contentType 指定擷取的內容的 MIME 型別。返回一個新的 Blob物件。

var blob = new Blob([`This is an example of Blob slice method`], {type: `text/plain`});
console.log(blob.size); // 39
var newBlob = blob.slice(10, 20, `text/plain`);
console.log(newBlob.size); // 10
複製程式碼

Blob 物件中讀取內容可以使用 FileReader. 下文會介紹。

2. File 物件

File建構函式

我們接觸的多數關於 File 的操作都是讀取,js也為我們提供了手動建立 File 物件的建構函式:File(bits, name[, options])

  • bits (required)
    ArrayBuffer,ArrayBufferView,Blob,或者 Array[string] — 或者任何這些物件的組合。這是 UTF-8 編碼的檔案內容。。

  • name [String] (required)
    檔名稱,或者檔案路徑.

  • options [Object] (optional)
    選項物件,包含檔案的可選屬性。可用的選項如下:

    • type: string, 表示將要放到檔案中的內容的MIME型別。預設值為 “ 。
    • lastModified: 數值,表示檔案最後修改時間的 Unix 時間戳(毫秒)。預設值為 Date.now()。

示例:

var file1 = new File([`text1`, `text2`], `test.txt`, {type: `text/plain`});
複製程式碼

根據已有的 blob 物件建立 File 物件:

var file2 = new File([blob], `test.png`, {type: `image/png`});
複製程式碼

File例項屬性

File 物件的例項內容不可見,但是有以下屬性可以訪問:

屬性名稱 讀/寫 描述
name 只讀 返回檔案的名稱.由於安全原因,返回的值並不包含檔案路徑 。
type 只讀 返回 File 物件所表示檔案的媒體型別(MIME)。例如 PNG 影像是 “image/png”.
lastModified 只讀 number, 返回所引用檔案最後修改日期,自 1970年1月1日0:00 以來的毫秒數。
lastModifiedDate 只讀 Date, 返回當前檔案的最後修改日期,如果無法獲取到檔案的最後修改日期,則使用當前日期來替代。

示例:

<input type="file" id=`file`>
複製程式碼
document.getElementById(`file`).addEventListener(`change`, function(event){
  const file = this.files[0];
  if (file) {
    console.log(file.name);
    console.log(file.size);
    console.log(file.lastModified);
    console.log(file.lastModifiedDate);
  }
});
複製程式碼

備註: 基於當前的實現,瀏覽器不會實際讀取檔案的位元組流,來判斷它的媒體型別。它基於檔案擴充套件來假設;將PNG 影像檔案的字尾名重新命名為 .txt,那麼讀取的該檔案的 type 屬性值為 “text/plain”, 而不是 “image/png” 。而且,file.type 僅僅對常見檔案型別可靠。例如影像、文件、音訊和視訊。不常見的副檔名會返回空字串。開發者最好不要依靠這個屬性,作為唯一的驗證方案。

File例項方法

  • slice([start[, end[, contentType]]])

File 物件沒有定義額外的方法,由於繼承了 Blob 物件,也就繼承了 slice方法,用法同上文 Blobslice 方法。

FileReader, URL.createObjectURL(), createImageBitmap(), 及 XMLHttpRequest.send() 都能處理 Blob 和 File。

3. FileReader 物件

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

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

FileReader建構函式

var reader = new FileReader()

建構函式不需要傳入引數,返回一個 FileReader 的例項。FileReader 繼承 EventTarget物件。

FileReader例項屬性

屬性名稱 讀/寫 描述
error 只讀 DOMException 的例項,表示在讀取檔案時發生的錯誤 。
result 只讀 檔案的內容,該屬性僅在讀取操作完成後(load)後才有效,格式取決於讀取方法
readyState 只讀 表示讀取檔案時狀態的數字

備註: readeyState的取值如下:

常量名 描述
0 EMPTY 還沒有載入任何資料
1 LOADING 資料正在被載入
2 DONE 已完成全部的讀取請求.

使用示例:

var reader = new FileReader();
console.log(reader.error);       // null
console.log(reader.result);      // null
console.log(reader.readyState);  // 0
console.log(reader.EMPTY);       // 0
console.log(reader.LOADING);     // 1
console.log(reader.DONE);        // 2
複製程式碼

EMPTYLOADINGDONE 這三個屬性同時存在於 FileReader 和它的的原型物件上,因此例項上有這三個屬性,FileReader 物件本身也有這三個屬性:

console.log(FileReader.EMPTY);   // 0
console.log(FileReader.LOADING); // 1
console.log(FileReader.DONE);    // 2
複製程式碼

FileReader事件

檔案的讀取是一個非同步的過程,和 XMLHttpRequest 物件一樣,在讀取操作過程中會觸發一系列事件。

事件名稱 描述 使用示例
abort 讀取操作被中斷時觸發。 reader.onabort = function(event) {}
error 在讀取操作發生錯誤時觸發。 reader.onerror = function(event) {}
load 讀取操作完成時觸發。 reader.addEventListener(`load`, function(event) {})
loadstart 讀取操作開始時觸發。 reader.onloadstart = function(event) {}
loadend 讀取操作結束時(要麼成功,要麼失敗)觸發。 reader.onloadend = function(event) {}
progress 在讀取Blob時觸發。 reader.onprogress = function(event) {}

FileReader例項方法

FileReader 的例項具有以下可操作的方法:

方法名稱 描述 使用示例
abort() 手動終止讀取操作,只有當 readyState 為 1 時才能呼叫,呼叫後,readyState 值為 2 reader.abort()
readAsArrayBuffer(blob) 讀取指定的 BlobFile 物件。讀取操作完成後(觸發loadend事件),result屬性將包含一個 ArrayBuffer 物件表示所讀取的檔案的資料。 reader.readAsArrayBuffer(blob)
readAsDataURL(blob) 讀取指定的 BlobFile 物件。讀取操作完成後(觸發loadend事件),result屬性將包含一個 data:URL 格式的字串(base64編碼) reader.readAsArrayBuffer(file)
readAsBinaryString(blob) 已廢棄,用 readAsArrayBuffer 代替
readAsText(blob[, encoding]) 將 Blob 或者 File 物件轉根據特殊的編碼格式轉化為內容(字串形式), 預設編碼是 utf-8 reader.readAsArrayBuffer(blob)

讀取本地圖片示例:

<input type="file" id=`file` accept="image/png, image/jpg, image/jpeg, image/gif" />><br />>
<img src="" alt="Image preview...">
複製程式碼
var preview = document.querySelector(`img`);
var reader  = new FileReader();
reader.addEventListener("load", function () {
  preview.src = reader.result;
}, false);
document.getElementById(`file`).addEventListener(`change`, function (event) {
  var file = this.files[0];
  if (file) {
    reader.readAsDataURL(file);
  }
});
複製程式碼

讀取多個檔案示例 – CodePen

dataURL是base64編碼的資料格式,展示型別為字串,形如: data:image/jpeg;base64,/9j/4QXERXhpZgAATU...

dataURL 轉為 blob物件:

function dataURLToBlob (dataurl) {
  let arr = dataurl.split(`,`);
  let mime = arr[0].match(/:(.*?);/)[1];
  let bstr = atob(arr[1]);
  let n = bstr.length;
  let u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new Blob([u8arr], { type: mime });
}
複製程式碼

結合上例,根據已有的 <img> 物件建立一個 File 物件:

reader.addEventListener("load", function () {
  preview.src = reader.result;
  var blob = dataURLToBlob(reader.result);
  var newFile = new File([blob], `test.jpeg`, {type: blob.type});
  console.log(newFile.name); // test.jpeg
  console.log(newFile.type);
  console.log(newFile.size);
}, false);
複製程式碼

URL.createObjectURL

將圖片檔案轉換成 data:URL 格式供 <img> 元素展示,除了使用 fileReader.readAsDataURL外,還可以使用 URL.createObjectURL方法。
URL.createObjectURL(blob) 方法返回一個 blob: 開頭的字串,指向檔案在記憶體中的地址。

<input type="file" id=`file` accept="image/png, image/jpg, image/jpeg, image/gif" /><br />
<img src="" alt="Image preview...">
複製程式碼
var preview = document.querySelector(`img`);
document.getElementById(`file`).addEventListener(`change`, function (event) {
  var file = this.files[0];
  if (file) {
    preview.src = URL.createObjectURL(file);
  }
});
複製程式碼

綜合例項

綜合以上,可以實現一個簡單的貼上圖片並顯示的功能。HTML5提供的內容編輯功能,可以貼上部分圖片,例如從網頁上覆制的圖片。但是使用截圖工具擷取的圖片無法貼上顯示,而且從網頁上覆制的圖片會帶有原來的樣式,其圖片url也是原來圖片的指向。我們使用以下程式碼可以統一這兩種貼上操作,實現統一的效果。

<div id="comment" contenteditable></div>
複製程式碼
#comment{
  border: 1px solid #ccc;
  min-height: 500px;
  padding: 10px;
}
#comment:focus {
  border-color: #ccc;
  outline: none;
}
.img-paste {
  max-width: 100%;
}
複製程式碼
var comment = document.getElementById(`comment`);
comment.addEventListener(`paste`, function(event) {
  console.log(event);
  var item = event.clipboardData.files[0];
  if (item && /image/.test(item.type)) {
    var img = new Image();
    img.src = URL.createObjectURL(item);
    img.className = `img-paste`;
    this.appendChild(img);
    event.preventDefault();
  }
}, false);
複製程式碼

例項效果可以檢視這裡

參考連結

相關文章