在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传回的二进制数据,以后了再研究。