在html5以前,ajax上傳文件算是一個比較麻煩的事,要是想顯示一下上傳進度就更不容易。遇到這種情況往往需要借助於第三方插件,比如jquery.fileupload.js。如今html5已經技術已經變成一個非常流行、非常新潮的技術了,各個瀏覽器廠商也實現了不少的html5規范,如今文件上傳有了html5的支持已經變的相當容易了,我自己嘗試了一下用javascript原生的api來實現ajax上傳文件,為了頁面不至於太丑,我使用了bootstrap的一些組件,先上效果圖:

實現的功能介紹:
- ajax無刷新上傳
- 客戶端限制單文件大小為400MB
- 支持多個文件上傳
- 進度條顯示上傳進度
引用的類庫:
- servlet3.0(需要使用tomcat7及以上版本的web服務器)
- bootstrap v3.3.2
- jquery 1.11
頁面:
<!DOCTYPE html> <html> <head> <title>使用html5特性進行ajax上傳</title> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="this is my page"> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <script type="text/javascript" src="js/jquery-1.11.2.min.js"></script> <script type="text/javascript" src="js/bootstrap.min.js"></script> <link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"> <style type="text/css"> body{ padding:20px; } </style> <script type="text/javascript"> function upload(){ if(!window.FormData){ $("#msg").html("你的瀏覽器不支持。"); $(".alert").show(); } //獲得原生的form表單對象,等價於document.forms[0] var form = document.getElementById("uploadForm"); //單個文件超過400MB就不再上傳 var hasError = false; $("input[type='file']", form).each(function(index,file){ if(file.files.length > 0){ if(file.files[0].size > 400*1024*1024){ $("#msg").html("單個文件不能超過400MB。"); $(".alert").show(); hasError = true; return; } } }); if(hasError){ return; } //構造FormData對象用於發送數據 var formData = new FormData(form); var xhr = new XMLHttpRequest(); xhr.open("post", form.action, true); //設置請求超時 xhr.upload.timeout = 2000; xhr.upload.ontimeout = function (event){ $("#msg").html("請求超時!"); $(".alert").show(); }; //添加progress事件 xhr.upload.addEventListener("progress",function(e){ $(".progress-bar").addClass("active"); var howMuch = e.loaded / e.total; var p = parseFloat((howMuch * 100).toFixed(2))+"%"; $(".progress-bar").css("width",p).html(p); }, false); //上傳完成 xhr.upload.addEventListener("load", function(event){ $(".progress-bar").removeClass("active"); $("#msg").html("上傳完成!"); $(".alert").show(); }, false); xhr.upload.addEventListener("error", function(event){ $(".progress-bar").removeClass("active"); $("#msg").html("上傳出錯!"); $(".alert").show(); }, false); xhr.upload.addEventListener("abort", function(event){ $(".progress-bar").removeClass("active"); $("#msg").html("您取消了本次上傳。"); $(".alert").show(); }, false); xhr.send(formData); } //重置 function test(){ $("input[type='file']", document.forms[0]).each(function(index,file){ file.value = null; }); } </script> </head> <body> <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">文件上傳</h3> </div> <div class="panel-body"> <div class="alert alert-danger alert-dismissable" style="display:none;"> <button type="button" class="close" onclick="$('.alert').hide();" aria-hidden="true"> × </button> <span id="msg"> 錯誤!請進行一些更改。</span> </div> <form id="uploadForm" action="/ajaxUpload/fileUpload" method="post" enctype="multipart/form-data"> <div class="input-group"> <span class="input-group-addon" id="basic-addon1">文件一</span> <input name="file1" type="file" class="form-control" aria-describedby="basic-addon1"> </div> <p></p> <div class="input-group"> <span class="input-group-addon" id="basic-addon2">文件二</span> <input id="file2" name="file2" type="file" class="form-control" aria-describedby="basic-addon2"> </div> <p></p> <div class="input-group"> <span class="input-group-addon" id="basic-addon3">文件三</span> <input name="file3" type="file" class="form-control" aria-describedby="basic-addon3"> </div> <p></p> <div class="progress"> <div class="progress-bar progress-bar-danger progress-bar-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" style="width: 0%"> 0% </div> </div> </form> <div style="text-align: center;"> <button type="button" onclick="upload()" class="btn btn-primary" autocomplete="off"> 上傳 </button> <button type="button" onclick="test()" class="btn btn-primary" autocomplete="off"> 重置 </button> </div> </div> </div> </body> </html>
后台處理上傳請求的servlet:
1 package com.test.servlet; 2 3 import java.io.File; 4 import java.io.IOException; 5 import java.util.List; 6 7 import javax.servlet.ServletException; 8 import javax.servlet.http.HttpServlet; 9 import javax.servlet.http.HttpServletRequest; 10 import javax.servlet.http.HttpServletResponse; 11 import javax.servlet.http.Part; 12 13 import org.apache.catalina.core.ApplicationPart; 14 15 public class FileUploadServlet extends HttpServlet { 16 17 /** 18 * 19 */ 20 private static final long serialVersionUID = 4796039742918723240L; 21 22 @Override 23 protected void doGet(HttpServletRequest req, HttpServletResponse resp) 24 throws ServletException, IOException { 25 26 } 27 28 @Override 29 protected void doPost(HttpServletRequest req, HttpServletResponse resp) 30 throws ServletException, IOException { 31 req.setCharacterEncoding("utf-8"); 32 List<Part> parts = (List<Part>) req.getParts(); 33 for (Part part : parts) { 34 ApplicationPart p = (ApplicationPart) part; 35 if(p.getSize() != 0){ 36 String fileName = p.getSubmittedFileName(); 37 part.write("c:\\"+fileName); 38 } 39 } 40 } 41 }
web.xml :
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app version="3.0" 3 xmlns="http://java.sun.com/xml/ns/javaee" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 6 http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> 7 <display-name></display-name> 8 <welcome-file-list> 9 <welcome-file>index.jsp</welcome-file> 10 </welcome-file-list> 11 <servlet> 12 <servlet-name>fileUploadServlet</servlet-name> 13 <servlet-class>com.test.servlet.FileUploadServlet</servlet-class> 14 <multipart-config /> 15 </servlet> 16 <servlet-mapping> 17 <servlet-name>fileUploadServlet</servlet-name> 18 <url-pattern>/fileUpload</url-pattern> 19 </servlet-mapping> 20 </web-app>
用到的html5新特性:
FormData對象是XMLHttpRequest Level 2中新增的一個對象,它不但可以傳文本文件,還可以傳送二進制文件。它可以如下方式來構造:
var formData = new FormData();
還可以直接將一個form對象傳進去來構造(比如我上面的例子):
var formData = new FormData(document.forms[0]); //構造完成后還可以動態添加表單元素 formData.append("yang","yangguo");//添加更多....
是不是很方便?
下面來介紹下XMLHttpRequest Level 2的XMLHttpRequest對象的新特性:
- 可以使用FormData上傳二進制文件
- 可以設置 HTTP 請求的時限
- 可以獲取服務器端的二進制數據(這個是ajax下載了,我們這里主要介紹上傳)
- 可以獲得上傳/下載的進度(輕松實現進度條)
- 可以請求不同域名下的數據(跨域請求)
需要注意的是,如果是上傳文件,使用的對象是xhr.upload對象,下載使用的對象xhr,不管是上傳還是下載,與進度有關的事件如下:
- load 事件:傳輸成功完成。
- abort 事件:傳輸被用戶取消。
- error 事件:傳輸中出現錯誤。
- loadstart 事件:傳輸開始。
- loadEnd 事件:傳輸結束,但是不知道成功還是失敗。
- progress 事件:在傳輸期間持續不斷觸發直到傳輸完成。
我們這里用到load事件,傳輸完成后給用戶提示信息:傳輸完成,我們還監聽progress事件,通過progress事件上的事件對象event來獲得傳輸數據進度:
1 //事件對象e 2 e.loaded //表示當前傳輸了多少個字節 3 e.total //表示當前傳輸的這個文件總共有多少個字節 4 //那么通過兩數相除可以得到傳輸的百分比,保留小數點后兩位有效數字 5 var howMuch = e.loaded / e.total; 6 var p = parseFloat((howMuch * 100).toFixed(2))+"%";
那么得到百分比我們就更新進度條就可以了。
XMLHttpRequest 2.0也添加了File API,通過File接口可以獲得上傳文件的大小,通過這個我們可以限制單個文件的大小。通過下面方式獲得文件大小:
fileElement.files[0].size //文件大小,fileElement是一個<input type='file'/>的DOM對象 //fileElement.files是一個FileList對象,因為只有第一個所以取第一個,為什么是個list呢,可能是支持拖拽式的文件上傳拖拽多個文件進行上傳
File繼承自Blob,Blob代表一個不可更改的二進制文件對象(原文:A Blob object represents a file-like object of immutable, raw data),File繼承自Blob的屬性Blob.size 和Blob.type,其屬性都是只讀屬性,File.size表示文件的總字節數,其自身屬性還有:
- File.lastModifiedDate 文件的修改時間
- File.name input標簽的name屬性
我們使用size屬性取得文件占用的字節數和400MB比較,大於400MB就不上傳,提示文件過大。
另外還有還添加的還有FileReader來處理ajax傳回的二進制數據,以后了再研究。
