基於HTML5的移動Web應用——文件操作

過去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格式等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章