Ext4.2结合Spring MVC实现文件上传显示进度
1.实现原理:
在大文件上传时显示上传进度是很有必要的,不能让用户感觉到陷入无穷的等待中,或感觉程序已经卡死。为此我们可以在session中存一个上传进度的变量,在文件上传的过程中实时的去修改这个值,这就需要在后台重写Spring MVC自带的上传解析类,每上传一定的字节数就修改一下session中的进度,在前台通个AJAX请求每隔一定的时间去获取这个值显示给用户,这样就达到了显示上传进度的需求,不过这样频繁的请求服务器无疑是增加了服务器的压力,在没有使用任何第三方上传组件的情况下只能如此。
2.测试结果:
没有出现任何内存溢出或奔溃的情况。
前台效果和后台打印日志:
3.代码
①CustomMultipartResolver.java,Spring默认的文件上传类是CommonsMultipartResolver,我们需要自定义一个类CustomMultipartResolver继承它.
package com.upload; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItemFactory; 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.web.multipart.MaxUploadSizeExceededException; import org.springframework.web.multipart.MultipartException; import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.multipart.commons.CommonsMultipartResolver; public class CustomMultipartResolver extends CommonsMultipartResolver { private HttpServletRequest request; protected FileUpload newFileUpload(FileItemFactory fileItemFactory) { ServletFileUpload upload = new ServletFileUpload(fileItemFactory); if (request != null) { HttpSession session = request.getSession(); MyProgressListener uploadProgressListener = new MyProgressListener(session); upload.setProgressListener(uploadProgressListener); } return upload; } public MultipartHttpServletRequest resolveMultipart( HttpServletRequest request) throws MultipartException { this.request = request;// 获取到request,要用到session return super.resolveMultipart(request); } @Override public MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException { String encoding = determineEncoding(request); FileUpload fileUpload = prepareFileUpload(encoding); //设置监听器 MyProgressListener progressListener = new MyProgressListener(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); } } }
②spring-servlet.xml,XML配置
<!-- 文件上传配置 --> <!-- <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="1048576000"/> <property name="maxInMemorySize" value="4096"/> <property name="defaultEncoding" value="UTF-8"></property> </bean> --> <bean id="multipartResolver" class="com.upload.CustomMultipartResolver"> <property name="maxUploadSize" value="1048576000"/> <property name="maxInMemorySize" value="4096"/> <property name="defaultEncoding" value="UTF-8"></property> </bean>
③MyProgressListener.java,文件上传监听器,需要实现ProgressListener接口
package com.upload; import javax.servlet.http.HttpSession; import org.apache.commons.fileupload.ProgressListener; public class MyProgressListener implements ProgressListener { private HttpSession session; public MyProgressListener() { } public MyProgressListener(HttpSession session) { this.session = session; ProgressEntity ps = new ProgressEntity(); session.setAttribute("upload_ps", ps); } public void setSession(HttpSession session){ this.session = session; } @Override public void update(long pBytesRead, long pContentLength, int pItems) { ProgressEntity ps = (ProgressEntity) session.getAttribute("upload_ps"); ps.setpBytesRead(pBytesRead); ps.setpContentLength(pContentLength); ps.setpItems(pItems); //更新 session.setAttribute("upload_ps", ps); // System.out.println("当前进度:"+ps.getpBytesRead()); } }
④ProgressEntity.java 包含上传信息的实体
package com.upload; public class ProgressEntity { private long pBytesRead = 0L; //到目前为止读取文件的比特数 private long pContentLength = 0L; //文件总大小 private int pItems; //目前正在读取第几个文件 public long getpBytesRead() { return pBytesRead; } public void setpBytesRead(long pBytesRead) { this.pBytesRead = pBytesRead; } public long getpContentLength() { return pContentLength; } public void setpContentLength(long pContentLength) { this.pContentLength = pContentLength; } public int getpItems() { return pItems; } public void setpItems(int pItems) { this.pItems = pItems; } @Override public String toString() { return "ProgressEntity [pBytesRead=" + pBytesRead + ", pContentLength=" + pContentLength + ", pItems=" + pItems + "]"; } }
⑤Action中的方法,上传方法任然不用变,还是原来的Spring上传方法,只需修改XML中的上传解析器.改成前面自定义的即可,增加一个获取上传进度的方法。SuccessBean是封装了返回前台的信息:包含success和msg两个字段。
@ResponseBody @RequestMapping(value = "/getProgress", method = RequestMethod.GET) public SuccessBean getProgress(HttpServletRequest request, HttpServletResponse response){ if(request.getSession().getAttribute("upload_ps")==null){ return new SuccessBean(true,"0"); } ProgressEntity ps = (ProgressEntity)request.getSession().getAttribute("upload_ps"); Double percent = 0d; if(ps.getpContentLength() != 0L){ percent = (double)ps.getpBytesRead()/(double)ps.getpContentLength()*1.0d; //百分比 if(percent != 0d){ DecimalFormat df = new DecimalFormat("0.00"); percent = Double.parseDouble(df.format(percent)); } } logger.info("当前上传进度:" + percent.toString()); return new SuccessBean(true, percent.toString()); }
⑥前台就更简单了,只需一个FormPanel添加一个filefield组件,在表单提交后同时启动定时任务,每隔1秒从后台获取一下进度显示出来即可。