JavaScript File API實現檔案上傳預覽,api檔案上傳

來源:互聯網
上載者:User

JavaScript File API實現檔案上傳預覽,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 由一組 JavaScript 對象以及事件構成。賦予開發人員操作在 <input type=”file” … /> 檔案選擇控制項中選定檔案的能力。圖 1 展示了 File API 所有的 JavaScript 的組合關係。

類型 FileList 包含一組 File 對象。通常 FileList 對象可以從表單中的檔案域(<input type=”file” .../>)中拿取。Blob 對象代表瀏覽器所能讀取的一組原始二進位流。Blob 對象中,屬性 size 表示流的大小。函數 slice() 可以將一個長的 Blob 對象分割成小塊。File 對象繼承自 Blob 對象,在 Blob 對象基礎上增加了和 File 相關的屬性。其中,屬性 name 表示檔案的名字,這個名字去掉了檔案的路徑資訊,而只保留了檔案名稱。屬性 type 表示檔案的 MIME 類型。屬性 urn 則代表這個檔案的 URN 資訊。為完成檔案讀取的操作,一個 FileReader 對象執行個體會關聯 File 或 Blob 對象,並提供三種不同的檔案讀取函數以及 6 種事件。

檔案讀取函數具體內容:
readAsBinaryString()  讀取檔案內容,讀取結果為一個 binary string。檔案每一個 byte 會被表示為一個 [0..255] 區間內的整數。函數接受一個 File 對象作為參數。
readAsText()  讀取檔案內容,讀取結果為一串代表檔案內容的文本。函數接受一個 File 對象以及文本編碼名稱作為參數。
readAsDataURL  讀取檔案內容,讀取結果為一個 data: 的 URL。DataURL 由 RFC2397 定義。
檔案讀取事件具體內容:
事件名稱 事件說明
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 展示了運行中的示範。

我們逐步展示其中的代碼。清單 1 給出了代碼的 HTML 部分。
清單 1 範例程式碼的 HTML 部分

 <body>  <h1>File API Demo</h1>  <p>  <!-- 用於檔案上傳的表單元素 -->  <form name="demoForm" id="demoForm" method="post" enctype="multipart/form-data"  action="javascript: uploadAndSubmit();">  <p>Upload File: <input type="file" name="file" /></p>  <p><input type="submit" value="Submit" /></p>  </form>  <div>Progessing (in Bytes): <span id="bytesRead">  </span> / <span id="bytesTotal"></span>  </div>  </p>  </body>

可以看到,我們用普通的 <form> 標籤來包含一個傳統的 <input type=”file” … /> 元素。在 <form> 中還有一個 submit 元素。在 <form> 之外有一些 <span> 元素用來表示已讀取和總共的資料量。<form> 的 action 屬性指向了一個 JavaScript 函數 uploadAndSubmit()。這個函數完成了讀取檔案並上傳的過程。函數代碼見清單 2。
清單 2 讀取檔案並上傳的 JavaScript 函數

 function uploadAndSubmit() {  var form = document.forms["demoForm"];    if (form["file"].files.length > 0) {  // 尋找表單域中的 <input type="file" ... /> 標籤 var file = form["file"].files[0];  // try sending  var reader = new FileReader(); reader.onloadstart = function() {  // 這個事件在讀取開始時觸發 console.log("onloadstart");  document.getElementById("bytesTotal").textContent = file.size;  }  reader.onprogress = function(p) {  // 這個事件在讀取進行中定時觸發 console.log("onprogress");  document.getElementById("bytesRead").textContent = p.loaded;  } reader.onload = function() {   // 這個事件在讀取成功結束後觸發 console.log("load complete");  } reader.onloadend = function() {   // 這個事件在讀取結束後,無論成功或者失敗都會觸發 if (reader.error) {  console.log(reader.error);  } else {  document.getElementById("bytesRead").textContent = file.size;  // 構造 XMLHttpRequest 對象,傳送檔案 Binary 資料 var xhr = new XMLHttpRequest();  xhr.open(/* method */ "POST",  /* target url */ "upload.jsp?fileName=" + file.name  /*, async, default to true */);  xhr.overrideMimeType("application/octet-stream");  xhr.sendAsBinary(reader.result);  xhr.onreadystatechange = function() {  if (xhr.readyState == 4) {  if (xhr.status == 200) {  console.log("upload complete");  console.log("response: " + xhr.responseText);  }  }  }  }  } reader.readAsBinaryString(file);  } else {  alert ("Please choose a file.");  }  }

在這個函數中,首先我們找到含有 <input type=”file” … /> 元素的 <form>,並找到含有上傳檔案資訊的 <input> 元素。如 <input> 元素中不含有檔案,說明使用者沒有選擇任何檔案,此時將報錯。
清單 3 找到 <input> 元素

 var form = document.forms["demoForm"]; if (form["file"].files.length > 0)  {  var file = form["file"].files[0]; … … }  else  {  alert ("Please choose a file.");  }

這裡,從 form[“file”].files 返回的物件類型即為提到的 FileList。我們從中拿取第一個元素。之後,我們構建 FileReader 對象:
 var reader = new FileReader();
在 onloadstart事件觸發時,填充頁面上表示讀取資料總量的 <span> 元素。參見清單 4
清單 4 onloadstart 事件

 reader.onloadstart = function()  {  console.log("onloadstart");  document.getElementById("bytesTotal").textContent = file.size;  }在 onprogress 事件觸發時,更新頁面上已讀取資料量的 <span> 元素。參見清單 5

清單 5 onprogress 事件

 reader.onprogress = function(p) {  console.log("onloadstart");  document.getElementById("bytesRead").textContent = p.loaded;  }

最後的 onloadend 事件中,如果沒有錯誤,我們將讀取檔案內容,並通過 XMLHttpRequest 的方式上傳。
清單 6 onloadend 事件

 reader.onloadend = function()  {  if (reader.error)  {  console.log(reader.error);  }  else  {  // 構造 XMLHttpRequest 對象,傳送檔案 Binary 資料 var xhr = new XMLHttpRequest();  xhr.open(/* method */ "POST",  /* target url */ "upload.jsp?fileName=" + file.name  /*, async, default to true */);  xhr.overrideMimeType("application/octet-stream");  xhr.sendAsBinary(reader.result); … … }  }

按照 File API 的規範,我們也可以將事件 onloadend 的處理拆分為事件 error 以及事件 load 的處理。
在這個樣本中,我們後台使用一個 JSP 來處理上傳。JSP 代碼如清單 7。
清單 7 處理上傳的 JSP 代碼

 <%@ page import="java.io.*" %><%   BufferedInputStream fileIn = new  BufferedInputStream(request.getInputStream());   String fn = request.getParameter("fileName");     byte[] buf = new byte[1024];//接收檔案上傳並儲存到 d:\  File file = new File("d:/" + fn);     BufferedOutputStream fileOut = new BufferedOutputStream(new  FileOutputStream(file));     while (true) {     // 讀取資料   int bytesIn = fileIn.read(buf, 0, 1024);       System.out.println(bytesIn);       if (bytesIn == -1)  {      break;    }  else  {      fileOut.write(buf, 0, bytesIn);    }   }     fileOut.flush();   fileOut.close();     out.print(file.getAbsolutePath());  %>

在這段 JSP 代碼中,我們從 POST 請求中接受檔案名稱字以及位元據。將位元據寫入到伺服器的“D:\”路徑中。並返迴文件的完整路徑。以上代碼可以在最新的 Firefox 3.6 中調試通過。
四、使用拖拽上傳檔案
前面我們介紹了怎樣通過 HTML5 的 File API 來讀取本地檔案內容並上傳到伺服器,通過這種方式已經能夠滿足大部分使用者的需求了。其中一個不足是使用者只能通過點擊“瀏覽”按鈕來逐個添加檔案,如果需要批量上傳檔案,會導致使用者體驗不是很友好。而在案頭應用中,使用者一般可以通過滑鼠拖拽的方式方便地上傳檔案。拖拽一直是 Web 應用程式的一個軟肋,一般瀏覽器都不提供對拖拽的支援。雖然 Web 程式員可以通過滑鼠的 mouseenter,mouseover 和 mouseout 等事件來實現拖拽效果,但是這種方式也只能使拖拽的範圍局限在瀏覽器裡面。一個好訊息是 HTML5 裡面不僅加入了 File API,而且加入了對拖拽的支援,Firefox 3.5 開始已經對 File API 和拖拽提供了支援。下面我們先簡要介紹一下拖拽的使用,然後用一個例子來說明如何通過拖拽上傳檔案。
1、拖拽簡介
拖拽一般涉及兩個對象:拖拽源和拖拽目標。
拖拽源:在 HTML5 草案裡如果一個對象可以作為源被拖拽,需要設定 draggable 屬性為 true 來標識該對象可作為拖拽源。然後偵聽來源物件的 dragstart 事件,在事件處理函數裡設定好 DataTransfer。在 DataTransfer 裡可以設定拖拽資料的類型和值。比如是純文字的值,可以設定類型為"text/plain",url 則把類型設定為"text/uri-list"。 這樣,目標對象就可以根據期望的類型來選擇資料了。
拖拽目標:一個拖拽目標必須偵聽 3 個事件。
dragenter:目標對象通過響應這個事件來確定是否接收拖拽。如果接收則需要取消這個事件,停止時間的繼續傳播。
dragover:通過響應這個事件來顯示拖拽的提示效果。
drop:目標對象通過響應這個事件來處理拖拽資料。在下面的例子裡我們將在 drop 事件的處理函數裡擷取 DataTransfer 對象,取出要上傳的檔案。
由於本文主要介紹 File API,對這部分不作詳細解釋,感興趣的讀者可以參考 HTML5 草案(見參考資料)。
2、拖拽上傳檔案執行個體
下面以一個較為具體的例子說明如何結合拖拽和 File API 來上傳文檔。由於直接和案頭互動,所以我們不需要處理拖拽源,直接在目標對象裡從 DataTransfer 對象擷取資料即可。
首先,我們需要建立一個目標容器用來接收拖拽事件,添加一個 div 元素即可。然後用一個列表來展示上傳檔案的縮圖,進度條及檔案名稱。參見清單 8 的 HTML 程式碼和圖 3 的。詳細代碼請參見附件中的 dnd.html 檔案。
清單 8 拖曳目標的 HTML 程式碼

 <div id="container">  <span>Drag and drop files here to upload.</span>  <ul id="fileList"></ul>  </div>

拖拽目標建立好之後,我們需要偵聽其對應的事件 dragenter,dragover 和 drop。在 dragenter 事件處理函數裡,我們只是簡單地清除檔案清單,然後取消 dragenter 事件的傳播,表示我們接收該事件。更加妥當的作法是判斷 DataTransfer 裡的資料是否為檔案,這裡我們假設所有拖拽源都是檔案。dragover 事件裡我們取消該事件,使用預設的拖拽顯示效果。在 drop 事件裡我們註冊了 handleDrop 事件處理函數來擷取檔案資訊並上傳檔案。清單 9 展示了這些事件處理函數。
清單 9 設定事件處理函數

 function addDNDListeners()  {  var container = document.getElementById("container");  var fileList = document.getElementById("fileList");  // 拖拽進入目標對象時觸發 container.addEventListener("dragenter", function(event)  {  fileList.innerHTML ='';  event.stopPropagation();  event.preventDefault();  }, false);  // 拖拽在目標對象上時觸發 container.addEventListener("dragover", function(event)  {  event.stopPropagation();  event.preventDefault();  }, false);  // 拖拽結束時觸發 container.addEventListener("drop", handleDrop, false);  }  window.addEventListener("load", addDNDListeners, false);

處理 drop 事件
使用者在拖拽結束時鬆開滑鼠觸發 drop 事件。在 drop 事件裡,我們可以通過 event 參數的 DataTransfer 對象擷取 files 資料,通過遍曆 files 數組可以擷取每個檔案的資訊。然後針對每個檔案,建立 HTML 元素來顯示縮圖,進度條和檔案名稱。File 對象的 getAsDataURL 可以將檔案內容以 URL 的形式返回,對圖片檔案來說可以用來顯示縮圖。要注意的一點是,在 drop 事件處理函數裡要取消事件的繼續傳播和預設處理函數,結束 drop 事件的處理。清單 10 展示了 drop 事件的處理代碼。
清單 10 drop 事件的處理

 function handleDrop(event)  {   // 擷取拖拽的檔案清單 var files = event.dataTransfer.files;  event.stopPropagation();  event.preventDefault();  var fileList = document.getElementById("fileList");  // 展示檔案縮圖,檔案名稱和上傳進度,上傳檔案 for (var i = 0; i < files.length; i++)  {  var file = files[i];  var li = document.createElement('li');  var progressbar = document.createElement('div');  var img = document.createElement('img');  var name = document.createElement('span');  progressbar.className = "progressBar";  img.src = files[i].getAsDataURL();  img.width = 32;  img.height = 32;  name.innerHTML = file.name;  li.appendChild(img);  li.appendChild(name);  li.appendChild(progressbar);  fileList.appendChild(li);  uploadFile(file, progressbar)  }  }

上傳檔案
我們可以通過 XMLHttpRequest 對象的 sendAsBinary 方法來上傳檔案,通過偵聽 upload 的 progress,load 和 error 事件來監測檔案上傳的進度,成功完成或是否發生錯誤。在 progress 事件處理函數裡我們通過計算已經上傳的比例來確定進度條的位置。參見清單 11。圖 4 展示了上傳檔案的。
清單 11 上傳檔案

 function uploadFile(file, progressbar)  {  var xhr = new XMLHttpRequest();  var upload = xhr.upload; var p = document.createElement('p');  p.textContent = "0%";  progressbar.appendChild(p);  upload.progressbar = progressbar;  // 設定上傳檔案相關的事件處理函數 upload.addEventListener("progress", uploadProgress, false);  upload.addEventListener("load", uploadSucceed, false);  upload.addEventListener("error", uploadError, false);  // 上傳檔案 xhr.open("POST", "upload.jsp?fileName="+file.name);  xhr.overrideMimeType("application/octet-stream");  xhr.sendAsBinary(file.getAsBinary());  }  function uploadProgress(event)  {  if (event.lengthComputable)  {   // 將進度換算成百分比 var percentage = Math.round((event.loaded * 100) / event.total);  console.log("percentage:" + percentage);  if (percentage < 100)  {  event.target.progressbar.firstChild.style.width = (percentage*2) + "px";  event.target.progressbar.firstChild.textContent = percentage + "%";  }  }  }  function uploadSucceed(event)  {  event.target.progressbar.firstChild.style.width = "200px";  event.target.progressbar.firstChild.textContent = "100%";  }  function uploadError(error)  {  alert("error: " + error);  }


本文通過對 File API 規範的講解,以及兩個展示其使用方法的例子,為大家提前揭示了作為未來 HTML5 重要組成部分的 JavaScript File API 的全貌。利用它,結合其他 HTML5 的新特性,比如 Drag&Drop,我們可以利用純 JavaScript 方案,為使用者提供更好使用體驗的 Web 應用程式,與此同時,這樣的一致化方案也使我們避免了以往跨瀏覽器支援所花費的巨大代價。相信 File API 的出現和廣泛應用,將會是未來的 Web 2.0 應用的大勢所趨。

您可能感興趣的文章:
  • jQuery外掛程式jcrop+Fileapi完美實現圖片上傳+裁剪+預覽的代碼分享
  • JavaScript File API檔案上傳預覽

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.