在瀏覽器中操作檔案,多數情況下用到的是 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中儲存的結束符不變。
- type,預設值為 “”,它代表了將會被放入到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
方法接收三個可選引數,start
和 end
都是數值,表示擷取的範圍,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
方法,用法同上文 Blob
的 slice
方法。
FileReader, URL.createObjectURL(), createImageBitmap(), 及 XMLHttpRequest.send() 都能處理 Blob 和 File。
3. FileReader 物件
FileReader
物件允許Web應用程式非同步讀取儲存在使用者計算機上的檔案(或原始資料緩衝區)的內容,使用File
或Blob
物件指定要讀取的檔案或資料。
其中 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
複製程式碼
EMPTY
、LOADING
、DONE
這三個屬性同時存在於 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) | 讀取指定的 Blob 或 File 物件。讀取操作完成後(觸發loadend 事件),result 屬性將包含一個 ArrayBuffer 物件表示所讀取的檔案的資料。 |
reader.readAsArrayBuffer(blob) |
readAsDataURL(blob) | 讀取指定的 Blob 或 File 物件。讀取操作完成後(觸發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);
}
});
複製程式碼
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);
複製程式碼
例項效果可以檢視這裡。