過去Web程序不能替代桌面程序的一個重要原因就在於瀏覽器對於文件操作API的缺失。照片處理中的裁剪、濾鏡,二維碼的讀取與識別,文檔的查看和編輯等,這些操作無一不依賴文件的操作,HTML5賦予了瀏覽器幾乎和本地程序同等強大的文件操作能力。
File API是HTML5在DOM標準中添加的功能,它允許Web內容在用戶授權的情況下選擇本地文件並讀取內容一通過 File,FileList 和FileReader等對象共同作用來實現。
選擇文件
1、通過表單選擇文件
由於Web環境的特殊性,爲了考慮文件安全問題,瀏覽器不允許JavaScript直接訪問文件系統,但可以通過file類型的input元素或者拖放的方式選擇文件操作。
<input type="file" id="thisFile"/>
File表單可以讓用戶選取一個或者多個文件(multiple 屬性),通過FileAPI,可在用戶選擇文件後訪問到代表了所選文件列表的FileList對象,FileList 對象是一個類數組的對象,其中包含着一個或多個File對象。如果沒有multiple屬性或者用戶只選了一個文件,那麼只需要訪問FileList對象的第一個元素:
var filelist=document.getElementById('thisFile') .files;
var selectedFile-filelist[0];
使用input元素時,用戶在選擇文件後會觸發其change事件:
var inputElement=document.getElementById('thisFile')
inputElement. addEventListener ("change",handleFiles, false)
function handleFiles(){
var fileList=this.files
}
和其他類數組對象一樣,FileList 也有length屬性,可以輕鬆遍歷其File對象:
for (var i=0, numFiles=files.length ; i< numFiles; i++) {
var file=files[i]
……
}
File對象有3個很有用的屬性,包括了關於該文件的許多有用信息。
(1) name: 文件名,不包含路徑信息。
(2) size: 文件大小,以B爲單位。
(3) type:文件的MIME type。
需要注意的是,這3個屬性都是隻讀的。
2、通過拖曳來選擇文件
使用拖曳的方式來選擇文件的效果,需要通過訪問dataTransfer的files屬性來實現。
下面通過一個案例來演示具體效果,如示例所示。
【示例】 使用拖曳的方式選擇文件
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<style>
.dropzone{
width: 200px;
height: 100px;
border: 2px dashed #ddd;
text-align: center;
padding-top: 100px;
color: #999;
}
</style>
<body>
<div id="dropzone" class="dropzone">
拖曳文件到此處
</div>
<div id="output" class="output">
</div>
<script>
function getFileInfo(file) {
var aMultiples = ["KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"], sizeinfo;
var info = '文件名:' + file.name ;
// 計算文件大小的近似值
for (var nMultiple = 0, nApprox = file.size / 1024; nApprox > 1; nApprox /= 1024, nMultiple++) {
sizeinfo = nApprox.toFixed(3) + " " + aMultiples[nMultiple] + " (" + file.size + " bytes)";
}
info += ";大小:" + sizeinfo
info += ";類型:" + file.type
return info + '<br>'
}
var dropzone = document.getElementById('dropzone');
dropzone.addEventListener('drop', function (e) {
var html = '您一共選擇了 ' + e.dataTransfer.files.length + '個文件,文件信息如下:<br>';
[].forEach.call(e.dataTransfer.files, function (file) {
html += getFileInfo(file);
});
document.getElementById('output').innerHTML = html
e.preventDefault();
e.stopPropagation();
}, false);
dropzone.addEventListener('dragover', function (e) {
if (e.preventDefault) {
// 必須要阻止dragover的默認行爲(即不可drop),這樣才能進行drop操作。
// 否則不會觸發drop事件
e.preventDefault();
}
return false
}, false)
</script>
</body>
</html>
用Chrome瀏覽器訪問示例。
操作文件
1、FileReader 對象
前面講到表單或者dataTansier對象中的File類型的實例代表着這個文件,但是這個文件對象只能訪問到一些基本的信息(大小和文件名等),如果要訪問文件的具體內容,還得藉助FileReader對象。
FileReader對象可以將文件對象轉換爲字符串、DataURL對象或者二進制字符串等,以進行進一步操作。例如,在做圖片上傳功能時,可以先對選擇的圖片進行預覽或者剪裁,待用戶確認無誤後再進行上傳,可以節省許多不必要的帶寬。以前文的拖曳文件例子爲基礎,加上拖曳圖片預覽功能,代碼如示例所示。
【示例】 FileReader對象
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<style>
.dropzone {
width: 200px;
height: 100px;
border: 2px dashed #ddd;
text-align: center;
padding-top: 100px;
color: #999;
}
.preview img {
width: 100px;
height: 100px;
}
</style>
<body>
<div id="dropzone" class="dropzone">
拖拽文件到此處
</div>
<div id="preview" class="preview">
</div>
<script>
function handleFiles(files) {
var preview = document.getElementById('preview')
for (var i = 0; i < files.length; i++) {
var file = files[i]
// 用來過濾非圖片類型
var imageType = /image.*/
if (!file.type.match(imageType)) {
continue
}
// 只能動態創建img對象來進行預覽
var img = document.createElement("img")
// 將文件對象存起來
img.file = file
// 新建 FileRead 對象——是不是很簡單?
var reader = new FileReader()
// FileReader在讀取文件時是異步執行的(JS中許多對象都有類似API),因此需
要通過綁定其load事件來訪問文件讀取的結果
// 要注意,這裏使用了閉包,因爲img只保存當前函數(handleFiles)內的引用,
for循環並不會創建新的作用域
// 因此要通過一個閉包的形式拷貝一份img的引用,否則img在for循環結束後將
只引用最後一次創建的img元素。
reader.onload = (function(aImg) {
return function(e) {
// e.target.result 包含讀取到的 dataURL信息
aImg.src = e.target.result
// 將圖片插入當前文檔
preview.appendChild(aImg)
}
})(img)
reader.onprogress = function (e) {
console.log('當前已加載:' + (e.loaded / e.total * 100).toFixed(2)
+ '%')
}
// readAsDataURL方法將file對象讀取爲dataURL
reader.readAsDataURL(file)
}
}
var dropzone = document.getElementById('dropzone')
dropzone.addEventListener('drop', function (e) {
handleFiles(e.dataTransfer.files)
e.preventDefault()
e.stopPropagation()
}, false)
dropzone.addEventListener('dragover', function (e) {
if (e.preventDefault) {
e.preventDefault()
}
return false
}, false)
</script>
</body>
</html>
用瀏覽器訪問該頁面。
從上面的例子可以看到FileReader()的基本用法。readAsDataURL()方法用於讀取文件,它接收一個File 或者Blob對象的實例作爲參數,並將文件內容轉換爲一個base64編碼的URL字符串,並通過load事件將結果傳遞到e.target.result上。FileReader對象除了readAsDataURL()方法外,還有其他幾個方法用於讀取文件內容的操作。
(1) readAsArrayBuffer(Blob|File): 讀取文件,最後result屬性將包含ArrayBuffer對象以表示文件內容。ArrayBuffer 對象用來表示固定長度二進制數據的緩衝區。
(2) readAsBinaryString(Blob|File):讀取文件, result屬性包含文件的原始二進制數據。每個字節均由一個[0.255]範圍內的整數表示。
(3) readAsText(BloblFile,encoding): 以文本方式讀入文件,並可以指定返回數據的編碼,默認爲UTF-8。
(4) abort): 終止正在進行的讀取操作。如果FileReader 對象沒有進行讀取操作,調用此方法會拋出DOM_FILE_ABORT_ERR異常。
2、Blob 對象
以上讀取文件操作的方法有兩個共同點,一是都接受一個 Blob或File類型的對象。
var fileParts=['<a>hey man</a>'];
var myB1ob=new B1ob (fileParts,{ "type":"text/xml"});
Blob對象還支持slice() 方法,用於對數據進行切割:
var yourBlob = myB1ob.slice (10,20) ;
File對象同樣繼承了Blob的slice()方法,可以利用此方法對File對象預先進行分割,然後再讀取、上傳,最後在服務器端進行組裝——異步上傳的原理就是這樣。如果再記住分割點,這樣即使網絡中途斷掉,也可以在下次傳輸時從斷點續傳。
除了都接受Blob和File對象,這些方法另外一個共同點是,由於JavaScript本身基於事件驅動,這些和平臺相關的方法都是異步方法。即調用時立即返回,讀取文件操作完成後再觸發相應的load事件。
除了load事件,FileReader 對象還會調用這樣一些事件處理程序。
(1) onabort:當讀取操作被終止時調用(調用abort 方法)
(2) onerror: 當讀取操作發送錯誤時調用。
(3) onload: 當讀取操作成功完成時調用。
(4) onloadend:當讀取操作完成時調用,不管是成功還是失敗,該處理程序在onload或者onerror後調用。
(5) onloadstart: 當讀取操作將要開始之前調用。
(6) onprogress:在讀取數據過程中週期性調用。該事件爲最有用的事件,在加載較大的文件時,可以提供一個進度條讓用戶知道當前加載進度,不讓用戶產生焦躁感。
reader.onprogress= function(e) {
console.log('當前文件已加載'+ e.loaded/e. total*100.toFixed(2)+'%')
}
e.total存儲着當前文件的總大小(字節),e.loaded 表示當前文件已經加載了多少。
要想將圖片文件轉換成可以直接在HTML裏引用的URL,除了前文提到的readAsDataURL()方法,還可以使用window.URL .createObjectURL()方法:
var objectURL =window.URL. crea teObjectURL (file0bj) ;
objectURL最後會得到一個類似blob: null/a672ae4c- f84e- 45d2- -87ae-f45dc986d601的字符串,這個字符串可以直接被IMG等元素引用。
objectURL和dataURL一樣可以直接被img的src屬性引用,就像Windows平臺下的文件句柄或者Linux下的文件描述符,在使用完之後通常還要調用window.URL.revokeObjectURL()方法進行釋放,如果不顯示調用該方法,objectURL 將會在文檔卸載時自動釋放。對於前文的例子可以簡單修改爲URL對象版本:
function handleFiles (files) {
var preview=document.getElementById('preview')
for(var i=0;i<files.length;i++) {
var file=files [i]
……
var img=document.createElement("img")
img.src=window. URL.createObjectURL(file)
img.onload= function(e) {
window.URL.revoke0bjectURL(this.src)
}
Preview. appendChild (img)
}
}
有了操作文件的利器,讀者可以做一些有趣的功能,比如實現類似Photoshop中圖片處理的濾鏡或者讀取PDF文檔並轉換爲HTML格式等。