Ajax文件上传进度条
1 准备SSM的Maven环境
任意的ssm环境的项目,非maven的也可,但是注意在SpringMVC的Controller中使用@ResponseBody
注解的时候必须引入jackson
的依赖包,否则该注解无法使用.
<!-- jackson 版本 -->
<jackson.version>2.5.4</jackson.version>
<!-- springMVC依赖的jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
2 前端页面
前端页面的重点是ajax文件上传,因为一直无法获得文件的对象,所以在这里费的时间比较长,需要注意js获得文件对象的方式。
引入BootStrap的样式
此处非必须,不是BootStrap也不影响
<!-- 导入css样式 -->
<link href="${pageContext.request.contextPath}/static/bootstrap3/css/bootstrap.min.css" rel="stylesheet" />
<!-- 导入jQuery库 -->
<script type="text/javascript" src="${pageContext.request.contextPath}/static/bootstrap3/jquery/jquery-1.11.3.min.js" ></script>
<!-- 导入js库 -->
<script type="text/javascript" src="${pageContext.request.contextPath}/static/bootstrap3/js/bootstrap.min.js" ></script>
<style type="text/css">
fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}
legend{padding:.5em;border:0;width:auto}
</style>
上传表单
<fieldset>
<h1>Ajax文件上传进度条</h1>
<!--
ajax提交后form表单中如果存在button按钮的话ajax完成之后form表单
还是会自动提交
-->
<form action="${pageContext.request.contextPath}/pd/upload2.action"
method="post" id="form1" name="form1"
enctype="multipart/form-data"
onsubmit="return false;" role="form" class="form-inline">
<input type="file" name="fileObj" id="fileObj" class="form-group" />
</form>
<!-- 动画进度条 -->
<div id="pgID" style="display:block;width:300px;" >
<div class="progress progress-striped active" >
<div id="pdBar" class="progress-bar progress-bar-success" role="progressbar"
aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"
style="width:0%;">
<span id="pdNum" class="sr-only">40% 完成</span>
</div>
</div>
</div>
<div>
<button class="btn btn-primary" id="upload" >上传</button>
<button class="btn btn-danger" id="cancel" >取消</button>
</div>
<p>开始时间:<font id="startTime" color="red"></font></p>
<p>现在时间:<font id="currentTime" color="red"></font></p>
<p>已经传输了的时间(s):<font id="time" color="red"></font></p>
<p>传输速度(byte/s):<font id="velocity" color="red"></font></p>
<p>估计总时间:<font id="totalTime" color="red"></font></p>
<p>估计剩余时间:<font id="timeLeft" color="red"></font></p>
<p>上传百分比:<font id="percent" color="red"></font></p>
<p>已完成数:<font id="length" color="red"></font></p>
<p>总长度(M):<font id="totalLength" color="red"></font></p>
</fieldset>
JS代码部分
需要注意的是JS获取文件的时候var fileObj = $("#fileObj")[0].files[0];
必须这样写,否则取不到文件
<script type="text/javascript">
var timeInfo = 0;
$(function(){
$("#upload").click(function(){
uploadFile();
timeInfo = setInterval("getUploadInfo()",1000);
});
$("#cancel").click(function(){
$("#pdBar").css({"width":"0%"});
clearInterval(timeInfo);
});
});
//文件上传
function uploadFile(){
var fileObj = $("#fileObj")[0].files[0];//必须这样写,否则取不到文件
console.log(fileObj);
//$('#form1').serialize() 无法序列化二进制文件,这里采用formData上传
//需要浏览器支持:Chrome 7+、Firefox 4+、IE 10+、Opera 12+、Safari 5+。
var formData = new FormData(); // FormData 对象
// formData.append("author", "hooyes"); // 可以增加表单数据
formData.append("fileObj", fileObj); // 文件对象
console.log("开始上传..."+formData);
$.ajax({
type:"POST",
url:"${pageContext.request.contextPath}/pd/upload.action",
data:formData,
async: true,
cache: false,
dataType:"json",
contentType: false,/*必须false才会自动加上正确的Content-Type */
processData: false,/*必须false才会避开jQuery对 formdata 的默认处理XMLHttpRequest会对 formdata 进行正确的处理*/
success:function(res){
console.log(res);
if(res.tag === true){
clearInterval(timeInfo);
getUploadInfo();//修正得到文件上传信息
setPdWidth(100);//修正文件上传为100%
alert("上传成功!");
}else{
clearInterval(timeInfo);
alert("上传失败!");
}
},
error:function(){
clearInterval(timeInfo);
alert("上传错误!");
}
});
}
//打开进度条
function openProgess(){
/* var modalHeight=$(window).height() / 2 - $('#pgID').height() / 2;
$("#pgID").css({"display":"block","margin":modalHeight}); */
$("#pgID").css({"display":"block","width":"300px"});
}
//关闭进度条
function closeProgess(){
$("#pgID").css({"display":"none","width":"300px"});
$("#pdBar").css({"width":"0%"});
}
//得到上传文件进度信息
function getUploadInfo(){
$.ajax({
url:"${pageContext.request.contextPath}/pd/getInfo.action",
data:{time:new Date()},
type:"post",
dataType:"json",
cache:false,
success:function(res){
if(res.percent == 100){
clearInterval(timeInfo);
return;
}
console.log(res);
setPdWidth(res.percent);
setUploadInfo(res);
},
error:function(){
clearInterval(timeInfo);
console.log("得到上传文件信息出错!");
}
});
}
//设置上传文件进度信息
function setUploadInfo(res){
$("#startTime").text(res.startTime);
$("#currentTime").text(res.currentTime);
$("#time").text(res.time);
$("#velocity").text(res.velocity);
$("#totalTime").text(res.totalTime);
$("#timeLeft").text(res.timeLeft);
$("#percent").text(res.percent);
$("#length").text(res.length);
$("#totalLength").text(res.totalLength);
}
function setPdWidth(percent){
$("#pdBar").css({"width":percent+"%"});
}
</script>
3 后端代码
Progress实体类
创建一个进度条的实体类,此处省略getter和setter方法
/**
* 进度条的entity
* @author BartG
*
*/
public class Progress {
private long bytesRead;
private long contentLength;
private long items;
private long startTime = System.currentTimeMillis(); // 开始上传时间,用于计算上传速率
public Progress() { }
上传文件监听器
我们要获得上传文件的实时详细信息,必须继承org.apache.commons.fileupload.ProgressListener
类,获得信息的时候将进度条对象Progress
放在该监听器的session
对象中。
package com.bart.module.pb.listener;
import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.ProgressListener;
import org.springframework.stereotype.Component;
import com.bart.module.pb.entity.Progress;
@Component
public class FileUploadProgressListener implements ProgressListener {
private HttpSession session;
public void setSession(HttpSession session){
this.session=session;
Progress status = new Progress();//保存上传状态
session.setAttribute("status", status);
}
@Override
public void update(long bytesRead, long contentLength, int items) {
Progress status = (Progress) session.getAttribute("status");
status.setBytesRead(bytesRead);//已读取数据长度
status.setContentLength(contentLength);//文件总长度
status.setItems(items);//正在保存第几个文件
}
}
文件解析器
SpringMVC默认有一个文件解析器CommonsMultipartResolver
用来解析上传的文件,我们需要重写该类,自己重写的监听器放到org.apache.commons.fileupload.FileUpload
中,还需要将session
放到自定义的监听器中。
package com.bart.module.pb.resolver;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUpload;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import com.bart.module.pb.listener.FileUploadProgressListener;
public class CustomMultipartResolver extends CommonsMultipartResolver {
// 注入第二步写的FileUploadProgressListener
@Autowired
private FileUploadProgressListener progressListener;
public void setFileUploadProgressListener(FileUploadProgressListener progressListener) {
this.progressListener = progressListener;
}
@Override
public MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException {
String encoding = determineEncoding(request);
FileUpload fileUpload = prepareFileUpload(encoding);
progressListener.setSession(request.getSession());
fileUpload.setProgressListener(progressListener);
try {
List<FileItem> fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);
return parseFileItems(fileItems, encoding);
} catch (FileUploadBase.SizeLimitExceededException ex) {
throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex);
} catch (FileUploadException ex) {
throw new MultipartException("Could not parse multipart servlet request", ex);
}
}
}
配置SpringMVC的配置文件解析器
需要配置文件解析器为自定义的
<!-- ====================== 配置自定义的MutilpartResover解析器处理文件上传进度条 ====================== -->
<bean id="multipartResolver"
class="com.bart.module.pb.resolver.CustomMultipartResolver">
<property name="defaultEncoding" value="UTF-8"></property>
<property name="maxUploadSize" value="1024000000"></property>
</bean>
Controller代码
上传文件
@ResponseBody
@RequestMapping(value="/upload", method = RequestMethod.POST)
public Map<String, Object> index(HttpServletRequest request, HttpServletResponse response,
@RequestParam("fileObj") CommonsMultipartFile file) throws IOException {
Map<String, Object> result = new HashMap<>();
if (file.getSize() > 0) {
request.getSession().getServletContext().getRealPath(path);
String uploadPath = "D:/upload/";
try {
FileUtils.copyInputStreamToFile(file.getInputStream(),new File(uploadPath, file.getOriginalFilename()));
result.put("tag",true);
} catch (Exception e) {
result.put("tag",false);
}
}
return result;
}
得到上传文件详细信息
@ResponseBody
@RequestMapping("/getInfo")
public Map<String, Object> getUploadInfo(HttpServletRequest request, HttpServletResponse response){
Map<String, Object> result = new HashMap<>();
response.setHeader("Cache-Control", "no-store"); //禁止浏览器缓存
response.setHeader("Pragrma", "no-cache"); //禁止浏览器缓存
response.setDateHeader("Expires", 0); //禁止浏览器缓存
Progress status = (Progress) request.getSession(true).getAttribute("status");//从session中读取上传信息
if(status == null){
result.put("error", "没发现上传文件!");
return result;
}
long startTime = status.getStartTime(); //上传开始时间
long currentTime = System.currentTimeMillis(); //现在时间
long time = (currentTime - startTime) /1000 +1;//已经传顺的时间 单位:s
double velocity = status.getBytesRead()/time; //传输速度:byte/s
double totalTime = status.getContentLength()/velocity; //估计总时间
double timeLeft = totalTime -time; //估计剩余时间
int percent = (int)(100*(double)status.getBytesRead()/(double)status.getContentLength()); //百分比
double length = status.getBytesRead()/1024/1024; //已完成数
double totalLength = status.getContentLength()/1024/1024; //总长度 M
result.put("startTime",startTime);
result.put("currentTime",currentTime);
result.put("time",time);
result.put("velocity",velocity);
result.put("totalTime",totalTime);
result.put("timeLeft",timeLeft);
result.put("percent",percent);
result.put("length",length);
result.put("totalLength",totalLength);
return result;
}
总结
- 获得文件对象js写法
var fileObj = $("#fileObj")[0].files[0];
- 后台需要自定义文件解析器并使用
CommonsMultipartFile
来接收