天天看點

使用 JavaScript File API 實作檔案上傳

概述

以往對于基于浏覽器的應用而言,通路本地檔案都是一件頭疼的事情。雖然伴随着 Web 2.0 應用技術的不斷發展,JavaScript 正在扮演越來越重要的角色,但是出于安全性的考慮,JavaScript 一直是無法通路本地檔案的。于是,為了在浏覽器中能夠實作諸如拖拽并上傳本地檔案這樣的功能,我們就不得不求助于特定浏覽器所提供的各種技術了。比如對于 IE,我們需要通過 ActiveX 控件來擷取對本地檔案的通路能力,而對于 Firefox,同樣也要借助插件開發。由于不同浏覽器的技術實作不盡相同,為了讓程式能夠支援多浏覽器,我們的程式就會變得十分複雜而難于維護。不過現 在,這一切都因為 File API 的出現而得到了徹底的改變。

File API 是 Mozilla 向 W3C 送出的一個草案,旨在推出一套标準的 JavaScript API,其基本功能是實作用 JavaScript 對本地檔案進行操作。出于安全性的考慮,該 API 隻對本地檔案提供有限的通路。有了它,我們就可以很輕松的用純 JavaScript 來實作本地檔案的讀取和上傳了。目前,FireFox 3.6 是最先支援這一功能的浏覽器。除此以外,最新版本的 Google Chrome 浏覽器和 Safari 浏覽器也有相應的支援。File API 有望成為 W3C 目前正在制定的未來 HTML 5 規範當中的一部分。

本文後續章節将對 File API 做一個基本的介紹。并為讀者示範:如何利用 File API 實作基于 file input 控件的本地檔案讀取與上傳;以及利用拖拽實作從使用者系統批量上傳檔案的功能。通過本文,讀者可以了解最新的 File API 的概況以及使用,進而為建構具有更好使用者體驗的 Web 2.0 應用提供參考。

​​回頁首​​

File API 概覽

File API 由一組 JavaScript 對象以及事件構成。賦予開發人員操作在 <input type=”file” … /> 檔案選擇控件中標明檔案的能力。圖 1 展示了 File API 所有的 JavaScript 的組合關系。

圖 1. File API 相關類圖

類型 FileList 包含一組 File 對象。通常 FileList

對象可以從表單中的檔案域(<input type=”file” .../>)中拿取。Blob

對象代表浏覽器所能讀取的一組原始二進制流。Blob 對象中,屬性 size 表示流的大小。函數 slice() 可以将一個長的 Blob

對象分割成小塊。File 對象繼承自 Blob 對象,在 Blob 對象基礎上增加了和 File 相關的屬性。其中,屬性 name

表示檔案的名字,這個名字去掉了檔案的路徑資訊,而隻保留了檔案名。屬性

type 表示檔案的 MIME 類型。屬性 urn 則代表這個檔案的 URN 資訊。為完成檔案讀取的操作,一個 FileReader

對象執行個體會關聯 File 或 Blob 對象,并提供三種不同的檔案讀取函數以及 6 種事件。參見表 1 及表 2。

表 1. 檔案讀取函數

<col>

函數名稱

功能

readAsBinaryString()

讀取檔案内容,讀取結果為一個 binary string。檔案每一個 byte 會被表示為一個 [0..255] 區間内的整數。函數接受一個 File 對象作為參數。

readAsText()

讀取檔案内容,讀取結果為一串代表檔案内容的文本。函數接受一個 File 對象以及文本編碼名稱作為參數。

readAsDataURL

讀取檔案内容,讀取結果為一個 data: 的 URL。DataURL 由 RFC2397 定義,具體可以參考 http://www.ietf.org/rfc/rfc2397.txt。

表 2. 檔案讀取事件

事件名稱

事件說明

Onloadstart

檔案讀取開始時觸發。

Progress

當讀取進行中時定時觸發。事件參數中會含有已讀取總資料量。

Abort

當讀取被中止時觸發。

Error

當讀取出錯時觸發。

Load

當讀取成功完成時觸發。

Loadend

當讀取完成時,無論成功或者失敗都會觸發。

File API 簡單示例

接下來我們用一個簡單的例子展示 File API

的基本用法。這個示例包含兩個代碼檔案,index.html 包含 Web 端的 HTML 代碼和處理上傳的 JavaScript

代碼;upload.jsp 包含伺服器端接收上傳檔案的代碼。請參見附件中的 sourcecode.zip。在這個例子中,我們将顯示一個傳統的帶有

File 選擇域的表單。當使用者選擇檔案,點選送出後,我們使用 File API 讀取檔案内容,并通過 XMLHttpRequest

對象,用 Ajax 的方式将檔案上傳到伺服器上。圖 2 展示了運作中的示範截圖。

圖 2 File API 示範

我們逐漸展示其中的代碼。清單 1 給出了代碼的 HTML 部分。

清單 1 示例代碼的 HTML 部分

可以看到,我們用普通的 &lt;form&gt; 标簽來包含一個傳統的

&lt;input type=”file” … /&gt; 元素。在 &lt;form&gt; 中還有一個 submit 元素。在

&lt;form&gt; 之外有一些 &lt;span&gt; 元素用來表示已讀取和總共的資料量。&lt;form&gt; 的 action

屬性指向了一個 JavaScript 函數 uploadAndSubmit()。這個函數完成了讀取檔案并上傳的過程。函數代碼見清單 2。

清單 2 讀取檔案并上傳的 JavaScript 函數

在這個函數中,首先我們找到含有 &lt;input type=”file” …

/&gt; 元素的 &lt;form&gt;,并找到含有上傳檔案資訊的 &lt;input&gt; 元素。如 &lt;input&gt;

元素中不含有檔案,說明使用者沒有選擇任何檔案,此時将報錯。

清單 3 找到 &lt;input&gt; 元素

這裡,從 form[“file”].files 傳回的對象類型即為提到的 FileList。我們從中拿取第一個元素。之後,我們建構 FileReader 對象:

在 onloadstart事件觸發時,填充頁面上表示讀取資料總量的 &lt;span&gt; 元素。參見清單 4

清單 4 onloadstart 事件

在 onprogress 事件觸發時,更新頁面上已讀取資料量的 &lt;span&gt; 元素。參見清單 5

清單 5 onprogress 事件

最後的 onloadend 事件中,如果沒有錯誤,我們将讀取檔案内容,并通過 XMLHttpRequest 的方式上傳。

清單 6 onloadend 事件

按照 File API 的規範,我們也可以将事件 onloadend 的處理拆分為事件 error 以及事件 load 的處理。

在這個示例中,我們背景使用一個 JSP 來處理上傳。JSP 代碼如清單 7。

清單 7 處理上傳的 JSP 代碼

​<code>​//​</code>​接收檔案上傳并儲存到 ​<code>​d:\​</code>​

在這段 JSP 代碼中,我們從 POST 請求中接受檔案名字以及二進制資料。将二進制資料寫入到伺服器的“D:\”路徑中。并傳回檔案的完整路徑。以上代碼可以在最新的 Firefox 3.6 中調試通過。

使用拖拽上傳檔案

前面我們介紹了怎樣通過 HTML5 的 File API

來讀取本地檔案内容并上傳到伺服器,通過這種方式已經能夠滿足大部分使用者的需求了。其中一個不足是使用者隻能通過點選“浏覽”按鈕來逐個添加檔案,如果需要

批量上傳檔案,會導緻使用者體驗不是很友好。而在桌面應用中,使用者一般可以通過滑鼠拖拽的方式友善地上傳檔案。拖拽一直是 Web

應用的一個軟肋,一般浏覽器都不提供對拖拽的支援。雖然 Web 程式員可以通過滑鼠的 mouseenter,mouseover 和

mouseout

等事件來實作拖拽效果,但是這種方式也隻能使拖拽的範圍局限在浏覽器裡面。一個好消息是 HTML5 裡面不僅加入了 File

API,而且加入了對拖拽的支援,Firefox 3.5 開始已經對 File API

和拖拽提供了支援。下面我們先簡要介紹一下拖拽的使用,然後用一個例子來說明如何通過拖拽上傳檔案。

拖拽簡介

拖拽一般涉及兩個對象:拖拽源和拖拽目标。

拖拽源:在 HTML5 草案裡如果一個對象可以作為源被拖拽,需要設定

draggable 屬性為 true 來辨別該對象可作為拖拽源。然後偵聽源對象的 dragstart 事件,在事件處理函數裡設定好

DataTransfer。在 DataTransfer

裡可以設定拖拽資料的類型和值。比如是純文字的值,可以設定類型為"text/plain",url 則把類型設定為"text/uri-list"。

這樣,目标對象就可以根據期望的類型來選擇資料了。

拖拽目标:一個拖拽目标必須偵聽 3 個事件。

dragenter:目标對象通過響應這個事件來确定是否接收拖拽。如果接收則需要取消這個事件,停止時間的繼續傳播。

dragover:通過響應這個事件來顯示拖拽的提示效果。

drop:目标對象通過響應這個事件來處理拖拽資料。在下面的例子裡我們将在 drop 事件的處理函數裡擷取 DataTransfer 對象,取出要上傳的檔案。

由于本文主要介紹 File API,對這部分不作詳細解釋,感興趣的讀者可以參考 HTML5 草案(見參考資料)。

拖拽上傳檔案執行個體

下面以一個較為具體的例子說明如何結合拖拽和 File API 來上傳文檔。由于直接和桌面互動,是以我們不需要處理拖拽源,直接在目标對象裡從 DataTransfer 對象擷取資料即可。

首先,我們需要建立一個目标容器用來接收拖拽事件,添加一個 div 元素即可。然後用一個清單來展示上傳檔案的縮略圖,進度條及檔案名。參見清單 8 的 HTML 代碼和圖 3 的效果圖。詳細代碼請參見附件中的 dnd.html 檔案。

清單 8 拖曳目标的 HTML 代碼

圖 3 拖曳目标

拖拽目标建立好之後,我們需要偵聽其對應的事件 dragenter,dragover 和

drop。在 dragenter 事件處理函數裡,我們隻是簡單地清除檔案清單,然後取消 dragenter

事件的傳播,表示我們接收該事件。更加妥當的作法是判斷 DataTransfer

裡的資料是否為檔案,這裡我們假設所有拖拽源都是檔案。dragover 事件裡我們取消該事件,使用預設的拖拽顯示效果。在 drop

事件裡我們注冊了 handleDrop 事件處理函數來擷取檔案資訊并上傳檔案。清單

9 展示了這些事件處理函數。

清單 9 設定事件處理函數

處理 drop 事件

使用者在拖拽結束時松開滑鼠觸發 drop 事件。在 drop 事件裡,我們可以通過

event 參數的 DataTransfer 對象擷取 files 資料,通過周遊 files

數組可以擷取每個檔案的資訊。然後針對每個檔案,建立 HTML 元素來顯示縮略圖,進度條和檔案名稱。File 對象的 getAsDataURL

可以将檔案内容以 URL 的形式傳回,對圖檔檔案來說可以用來顯示縮略圖。要注意的一點是,在 drop

事件處理函數裡要取消事件的繼續傳播和預設處理函數,結束

drop 事件的處理。清單 10 展示了 drop 事件的處理代碼。

清單 10 drop 事件的處理

上傳檔案

我們可以通過 XMLHttpRequest 對象的 sendAsBinary

方法來上傳檔案,通過偵聽 upload 的 progress,load 和 error 事件來監測檔案上傳的進度,成功完成或是否發生錯誤。在

progress 事件處理函數裡我們通過計算已經上傳的比例來确定進度條的位置。參見清單 11。圖 4 展示了上傳檔案的效果圖。

清單 11 上傳檔案

圖 4 上傳檔案的效果圖

小結

本文通過對 File API

規範的講解,以及兩個展示其使用方法的例子,為大家提前揭示了作為未來 HTML5 重要組成部分的 JavaScript File API

的全貌。利用它,結合其他 HTML5 的新特性,比如 Drag&amp;Drop,我們可以利用純 JavaScript

方案,為使用者提供更好使用體驗的 Web 應用,與此同時,這樣的一緻化方案也使我們避免了以往跨浏覽器支援所花費的巨大代價。相信 File API

的出現和廣泛應用,将會是未來的 Web

2.0 應用的大勢所趨。

下載下傳

描述

名字

大小

下載下傳方法

示例代碼

sourcecode.zip

3KB

​​HTTP​​

​​關于下載下傳方法的資訊​​

繼續閱讀