欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript File API實(shí)現(xiàn)文件上傳預(yù)覽

 更新時(shí)間:2016年02月02日 14:26:17   投稿:lijiao  
這篇文章主要為大家介紹了JavaScript File API實(shí)現(xiàn)文件上傳預(yù)覽,F(xiàn)ile API將極大地方便 Web 端的文件上傳等操作,本文將介紹 File API的概況,并用兩個(gè)實(shí)例展示File API的應(yīng)用,感興趣的小伙伴們可以參考一下

一、概述
以往對(duì)于基于瀏覽器的應(yīng)用而言,訪問(wèn)本地文件都是一件頭疼的事情。雖然伴隨著 Web 2.0 應(yīng)用技術(shù)的不斷發(fā)展,JavaScript 正在扮演越來(lái)越重要的角色,但是出于安全性的考慮,JavaScript 一直是無(wú)法訪問(wèn)本地文件的。于是,為了在瀏覽器中能夠?qū)崿F(xiàn)諸如拖拽并上傳本地文件這樣的功能,我們就不得不求助于特定瀏覽器所提供的各種技術(shù)了。比如對(duì)于 IE,我們需要通過(guò) ActiveX 控件來(lái)獲取對(duì)本地文件的訪問(wèn)能力,而對(duì)于 Firefox,同樣也要借助插件開發(fā)。由于不同瀏覽器的技術(shù)實(shí)現(xiàn)不盡相同,為了讓程序能夠支持多瀏覽器,我們的程序就會(huì)變得十分復(fù)雜而難于維護(hù)。不過(guò)現(xiàn)在,這一切都因?yàn)?File API 的出現(xiàn)而得到了徹底的改變。

File API 是 Mozilla 向 W3C 提交的一個(gè)草案,旨在推出一套標(biāo)準(zhǔn)的 JavaScript API,其基本功能是實(shí)現(xiàn)用 JavaScript 對(duì)本地文件進(jìn)行操作。出于安全性的考慮,該 API 只對(duì)本地文件提供有限的訪問(wèn)。有了它,我們就可以很輕松的用純 JavaScript 來(lái)實(shí)現(xiàn)本地文件的讀取和上傳了。目前,F(xiàn)ireFox 3.6 是最先支持這一功能的瀏覽器。除此以外,最新版本的 Google Chrome 瀏覽器和 Safari 瀏覽器也有相應(yīng)的支持。File API 有望成為 W3C 目前正在制定的未來(lái) HTML 5 規(guī)范當(dāng)中的一部分。

二、File API 概覽
File API 由一組 JavaScript 對(duì)象以及事件構(gòu)成。賦予開發(fā)人員操作在 <input type=”file” … /> 文件選擇控件中選定文件的能力。圖 1 展示了 File API 所有的 JavaScript 的組合關(guān)系。

類型 FileList 包含一組 File 對(duì)象。通常 FileList 對(duì)象可以從表單中的文件域(<input type=”file” .../>)中拿取。Blob 對(duì)象代表瀏覽器所能讀取的一組原始二進(jìn)制流。Blob 對(duì)象中,屬性 size 表示流的大小。函數(shù) slice() 可以將一個(gè)長(zhǎng)的 Blob 對(duì)象分割成小塊。File 對(duì)象繼承自 Blob 對(duì)象,在 Blob 對(duì)象基礎(chǔ)上增加了和 File 相關(guān)的屬性。其中,屬性 name 表示文件的名字,這個(gè)名字去掉了文件的路徑信息,而只保留了文件名。屬性 type 表示文件的 MIME 類型。屬性 urn 則代表這個(gè)文件的 URN 信息。為完成文件讀取的操作,一個(gè) FileReader 對(duì)象實(shí)例會(huì)關(guān)聯(lián) File 或 Blob 對(duì)象,并提供三種不同的文件讀取函數(shù)以及 6 種事件。

文件讀取函數(shù)具體內(nèi)容:
readAsBinaryString()  讀取文件內(nèi)容,讀取結(jié)果為一個(gè) binary string。文件每一個(gè) byte 會(huì)被表示為一個(gè) [0..255] 區(qū)間內(nèi)的整數(shù)。函數(shù)接受一個(gè) File 對(duì)象作為參數(shù)。
readAsText()  讀取文件內(nèi)容,讀取結(jié)果為一串代表文件內(nèi)容的文本。函數(shù)接受一個(gè) File 對(duì)象以及文本編碼名稱作為參數(shù)。
readAsDataURL  讀取文件內(nèi)容,讀取結(jié)果為一個(gè) data: 的 URL。DataURL 由 RFC2397 定義。
文件讀取事件具體內(nèi)容:
事件名稱 事件說(shuō)明
Onloadstart  文件讀取開始時(shí)觸發(fā)。
Progress  當(dāng)讀取進(jìn)行中時(shí)定時(shí)觸發(fā)。事件參數(shù)中會(huì)含有已讀取總數(shù)據(jù)量。
Abort  當(dāng)讀取被中止時(shí)觸發(fā)。
Error  當(dāng)讀取出錯(cuò)時(shí)觸發(fā)。
Load  當(dāng)讀取成功完成時(shí)觸發(fā)。
Loadend  當(dāng)讀取完成時(shí),無(wú)論成功或者失敗都會(huì)觸發(fā)。

三、File API 簡(jiǎn)單示例
接下來(lái)我們用一個(gè)簡(jiǎn)單的例子展示 File API 的基本用法。這個(gè)示例包含兩個(gè)代碼文件,index.html 包含 Web 端的 HTML 代碼和處理上傳的 JavaScript 代碼;upload.jsp 包含服務(wù)器端接收上傳文件的代碼。請(qǐng)參見附件中的 sourcecode.zip。在這個(gè)例子中,我們將顯示一個(gè)傳統(tǒng)的帶有 File 選擇域的表單。當(dāng)用戶選擇文件,點(diǎn)擊提交后,我們使用 File API 讀取文件內(nèi)容,并通過(guò) XMLHttpRequest 對(duì)象,用 Ajax 的方式將文件上傳到服務(wù)器上。圖 2 展示了運(yùn)行中的演示截圖。

我們逐步展示其中的代碼。清單 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> 標(biāo)簽來(lái)包含一個(gè)傳統(tǒng)的 <input type=”file” … /> 元素。在 <form> 中還有一個(gè) submit 元素。在 <form> 之外有一些 <span> 元素用來(lái)表示已讀取和總共的數(shù)據(jù)量。<form> 的 action 屬性指向了一個(gè) JavaScript 函數(shù) uploadAndSubmit()。這個(gè)函數(shù)完成了讀取文件并上傳的過(guò)程。函數(shù)代碼見清單 2。
清單 2 讀取文件并上傳的 JavaScript 函數(shù)

 function uploadAndSubmit() { 
 var form = document.forms["demoForm"]; 
 
 if (form["file"].files.length > 0) { 
 // 尋找表單域中的 <input type="file" ... /> 標(biāo)簽
 var file = form["file"].files[0]; 
 // try sending 
 var reader = new FileReader();

 reader.onloadstart = function() { 
 // 這個(gè)事件在讀取開始時(shí)觸發(fā)
 console.log("onloadstart"); 
 document.getElementById("bytesTotal").textContent = file.size; 
 } 
 reader.onprogress = function(p) { 
 // 這個(gè)事件在讀取進(jìn)行中定時(shí)觸發(fā)
 console.log("onprogress"); 
 document.getElementById("bytesRead").textContent = p.loaded; 
 }

 reader.onload = function() { 
 // 這個(gè)事件在讀取成功結(jié)束后觸發(fā)
 console.log("load complete"); 
 }

 reader.onloadend = function() { 
 // 這個(gè)事件在讀取結(jié)束后,無(wú)論成功或者失敗都會(huì)觸發(fā)
 if (reader.error) { 
 console.log(reader.error); 
 } else { 
 document.getElementById("bytesRead").textContent = file.size; 
 // 構(gòu)造 XMLHttpRequest 對(duì)象,發(fā)送文件 Binary 數(shù)據(jù)
 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."); 
 } 
 }

在這個(gè)函數(shù)中,首先我們找到含有 <input type=”file” … /> 元素的 <form>,并找到含有上傳文件信息的 <input> 元素。如 <input> 元素中不含有文件,說(shuō)明用戶沒(méi)有選擇任何文件,此時(shí)將報(bào)錯(cuò)。
清單 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 返回的對(duì)象類型即為提到的 FileList。我們從中拿取第一個(gè)元素。之后,我們構(gòu)建 FileReader 對(duì)象:
 var reader = new FileReader();
在 onloadstart事件觸發(fā)時(shí),填充頁(yè)面上表示讀取數(shù)據(jù)總量的 <span> 元素。參見清單 4
清單 4 onloadstart 事件

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

清單 5 onprogress 事件

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

最后的 onloadend 事件中,如果沒(méi)有錯(cuò)誤,我們將讀取文件內(nèi)容,并通過(guò) XMLHttpRequest 的方式上傳。
清單 6 onloadend 事件

 reader.onloadend = function() 
 { 
 if (reader.error) 
 { 
 console.log(reader.error); 
 } 
 else 
 { 
 // 構(gòu)造 XMLHttpRequest 對(duì)象,發(fā)送文件 Binary 數(shù)據(jù)
 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 的規(guī)范,我們也可以將事件 onloadend 的處理拆分為事件 error 以及事件 load 的處理。
在這個(gè)示例中,我們后臺(tái)使用一個(gè) JSP 來(lái)處理上傳。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) { 
  // 讀取數(shù)據(jù)
  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 請(qǐng)求中接受文件名字以及二進(jìn)制數(shù)據(jù)。將二進(jìn)制數(shù)據(jù)寫入到服務(wù)器的“D:\”路徑中。并返回文件的完整路徑。以上代碼可以在最新的 Firefox 3.6 中調(diào)試通過(guò)。
四、使用拖拽上傳文件
前面我們介紹了怎樣通過(guò) HTML5 的 File API 來(lái)讀取本地文件內(nèi)容并上傳到服務(wù)器,通過(guò)這種方式已經(jīng)能夠滿足大部分用戶的需求了。其中一個(gè)不足是用戶只能通過(guò)點(diǎn)擊“瀏覽”按鈕來(lái)逐個(gè)添加文件,如果需要批量上傳文件,會(huì)導(dǎo)致用戶體驗(yàn)不是很友好。而在桌面應(yīng)用中,用戶一般可以通過(guò)鼠標(biāo)拖拽的方式方便地上傳文件。拖拽一直是 Web 應(yīng)用的一個(gè)軟肋,一般瀏覽器都不提供對(duì)拖拽的支持。雖然 Web 程序員可以通過(guò)鼠標(biāo)的 mouseenter,mouseover 和 mouseout 等事件來(lái)實(shí)現(xiàn)拖拽效果,但是這種方式也只能使拖拽的范圍局限在瀏覽器里面。一個(gè)好消息是 HTML5 里面不僅加入了 File API,而且加入了對(duì)拖拽的支持,F(xiàn)irefox 3.5 開始已經(jīng)對(duì) File API 和拖拽提供了支持。下面我們先簡(jiǎn)要介紹一下拖拽的使用,然后用一個(gè)例子來(lái)說(shuō)明如何通過(guò)拖拽上傳文件。
1、拖拽簡(jiǎn)介
拖拽一般涉及兩個(gè)對(duì)象:拖拽源和拖拽目標(biāo)。
拖拽源:在 HTML5 草案里如果一個(gè)對(duì)象可以作為源被拖拽,需要設(shè)置 draggable 屬性為 true 來(lái)標(biāo)識(shí)該對(duì)象可作為拖拽源。然后偵聽源對(duì)象的 dragstart 事件,在事件處理函數(shù)里設(shè)置好 DataTransfer。在 DataTransfer 里可以設(shè)置拖拽數(shù)據(jù)的類型和值。比如是純文本的值,可以設(shè)置類型為"text/plain",url 則把類型設(shè)置為"text/uri-list"。 這樣,目標(biāo)對(duì)象就可以根據(jù)期望的類型來(lái)選擇數(shù)據(jù)了。
拖拽目標(biāo):一個(gè)拖拽目標(biāo)必須偵聽 3 個(gè)事件。
dragenter:目標(biāo)對(duì)象通過(guò)響應(yīng)這個(gè)事件來(lái)確定是否接收拖拽。如果接收則需要取消這個(gè)事件,停止時(shí)間的繼續(xù)傳播。
dragover:通過(guò)響應(yīng)這個(gè)事件來(lái)顯示拖拽的提示效果。
drop:目標(biāo)對(duì)象通過(guò)響應(yīng)這個(gè)事件來(lái)處理拖拽數(shù)據(jù)。在下面的例子里我們將在 drop 事件的處理函數(shù)里獲取 DataTransfer 對(duì)象,取出要上傳的文件。
由于本文主要介紹 File API,對(duì)這部分不作詳細(xì)解釋,感興趣的讀者可以參考 HTML5 草案(見參考資料)。
2、拖拽上傳文件實(shí)例
下面以一個(gè)較為具體的例子說(shuō)明如何結(jié)合拖拽和 File API 來(lái)上傳文檔。由于直接和桌面交互,所以我們不需要處理拖拽源,直接在目標(biāo)對(duì)象里從 DataTransfer 對(duì)象獲取數(shù)據(jù)即可。
首先,我們需要?jiǎng)?chuàng)建一個(gè)目標(biāo)容器用來(lái)接收拖拽事件,添加一個(gè) div 元素即可。然后用一個(gè)列表來(lái)展示上傳文件的縮略圖,進(jìn)度條及文件名。參見清單 8 的 HTML 代碼和圖 3 的效果圖。詳細(xì)代碼請(qǐng)參見附件中的 dnd.html 文件。
清單 8 拖曳目標(biāo)的 HTML 代碼

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

拖拽目標(biāo)創(chuàng)建好之后,我們需要偵聽其對(duì)應(yīng)的事件 dragenter,dragover 和 drop。在 dragenter 事件處理函數(shù)里,我們只是簡(jiǎn)單地清除文件列表,然后取消 dragenter 事件的傳播,表示我們接收該事件。更加妥當(dāng)?shù)淖鞣ㄊ桥袛?DataTransfer 里的數(shù)據(jù)是否為文件,這里我們假設(shè)所有拖拽源都是文件。dragover 事件里我們?nèi)∠撌录褂媚J(rèn)的拖拽顯示效果。在 drop 事件里我們注冊(cè)了 handleDrop 事件處理函數(shù)來(lái)獲取文件信息并上傳文件。清單 9 展示了這些事件處理函數(shù)。
清單 9 設(shè)置事件處理函數(shù)

 function addDNDListeners() 
 { 
 var container = document.getElementById("container"); 
 var fileList = document.getElementById("fileList"); 
 // 拖拽進(jìn)入目標(biāo)對(duì)象時(shí)觸發(fā)
 container.addEventListener("dragenter", function(event) 
 { 
 fileList.innerHTML =''; 
 event.stopPropagation(); 
 event.preventDefault(); 
 }, false); 
 // 拖拽在目標(biāo)對(duì)象上時(shí)觸發(fā)
 container.addEventListener("dragover", function(event) 
 { 
 event.stopPropagation(); 
 event.preventDefault(); 
 }, false); 
 // 拖拽結(jié)束時(shí)觸發(fā)
 container.addEventListener("drop", handleDrop, false); 
 } 
 window.addEventListener("load", addDNDListeners, false);

處理 drop 事件
用戶在拖拽結(jié)束時(shí)松開鼠標(biāo)觸發(fā) drop 事件。在 drop 事件里,我們可以通過(guò) event 參數(shù)的 DataTransfer 對(duì)象獲取 files 數(shù)據(jù),通過(guò)遍歷 files 數(shù)組可以獲取每個(gè)文件的信息。然后針對(duì)每個(gè)文件,創(chuàng)建 HTML 元素來(lái)顯示縮略圖,進(jìn)度條和文件名稱。File 對(duì)象的 getAsDataURL 可以將文件內(nèi)容以 URL 的形式返回,對(duì)圖片文件來(lái)說(shuō)可以用來(lái)顯示縮略圖。要注意的一點(diǎn)是,在 drop 事件處理函數(shù)里要取消事件的繼續(xù)傳播和默認(rèn)處理函數(shù),結(jié)束 drop 事件的處理。清單 10 展示了 drop 事件的處理代碼。
清單 10 drop 事件的處理

 function handleDrop(event) 
 { 
 // 獲取拖拽的文件列表
 var files = event.dataTransfer.files; 
 event.stopPropagation(); 
 event.preventDefault(); 
 var fileList = document.getElementById("fileList"); 
 // 展示文件縮略圖,文件名和上傳進(jìn)度,上傳文件
 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) 
 } 
 }

上傳文件
我們可以通過(guò) XMLHttpRequest 對(duì)象的 sendAsBinary 方法來(lái)上傳文件,通過(guò)偵聽 upload 的 progress,load 和 error 事件來(lái)監(jiān)測(cè)文件上傳的進(jìn)度,成功完成或是否發(fā)生錯(cuò)誤。在 progress 事件處理函數(shù)里我們通過(guò)計(jì)算已經(jīng)上傳的比例來(lái)確定進(jìn)度條的位置。參見清單 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; 
 // 設(shè)置上傳文件相關(guān)的事件處理函數(shù)
 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) 
 { 
 // 將進(jìn)度換算成百分比
 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); 
 }


本文通過(guò)對(duì) File API 規(guī)范的講解,以及兩個(gè)展示其使用方法的例子,為大家提前揭示了作為未來(lái) HTML5 重要組成部分的 JavaScript File API 的全貌。利用它,結(jié)合其他 HTML5 的新特性,比如 Drag&Drop,我們可以利用純 JavaScript 方案,為用戶提供更好使用體驗(yàn)的 Web 應(yīng)用,與此同時(shí),這樣的一致化方案也使我們避免了以往跨瀏覽器支持所花費(fèi)的巨大代價(jià)。相信 File API 的出現(xiàn)和廣泛應(yīng)用,將會(huì)是未來(lái)的 Web 2.0 應(yīng)用的大勢(shì)所趨。

更多精彩內(nèi)容請(qǐng)參考專題《ajax上傳技術(shù)匯總》,《javascript文件上傳操作匯總》《jQuery上傳操作匯總》進(jìn)行學(xué)習(xí)。

相關(guān)文章

最新評(píng)論