天天看點

基于HTML5的移動Web應用——檔案操作選擇檔案操作檔案

過去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元立即加入學習!有需要的朋友戳:

騰訊課堂測試技術學習位址

歡迎轉載,但未經作者同意請保留此段聲明,并在文章頁面明顯位置給出原文連結。

繼續閱讀