過去Web程式不能替代桌面程式的一個重要原因就在于浏覽器對于檔案操作API的缺失。照片進行中的裁剪、濾鏡,二維碼的讀取與識别,文檔的檢視和編輯等,這些操作無一不依賴檔案的操作,HTML5賦予了浏覽器幾乎和本地程式同等強大的檔案操作能力。
File API是HTML5在DOM标準中添加的功能,它允許Web内容在使用者授權的情況下選擇本地檔案并讀取内容一通過 File,FileList 和FileReader等對象共同作用來實作。
選擇檔案
1、通過表單選擇檔案
由于Web環境的特殊性,為了考慮檔案安全問題,浏覽器不允許JavaScript直接通路檔案系統,但可以通過file類型的input元素或者拖放的方式選擇檔案操作。
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() 方法,用于對資料進行切割:
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()方法:
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格式等。
超全面的測試IT技術課程,0元立即加入學習!有需要的朋友戳:
騰訊課堂測試技術學習位址
歡迎轉載,但未經作者同意請保留此段聲明,并在文章頁面明顯位置給出原文連結。