一、 引言
基于浏览器的文件上传,特别是对于通过<input type="file">标签包含到Web页面来实现上传的情况,还存在较严重的性能问题。我们知道,超过10MB的上传文件经常导致一种非常痛苦的用户体验。一旦用户提交了文件,在浏览器把文件上传到服务器的过程中,界面看上去似乎处于静止状态。由于这一切发生在后台,所以许多没有耐心的用户开始认为服务器"挂"了,因而再次提交文件,这当然使得情况变得更糟糕。
为了尽可能使得文件上传感觉更友好些,一旦用户提交文件,许多站点将显示一个中间过程动画(例如一旋转图标)。尽管这一技术在上传提交到服务器时起一些作用,但它还是提供了太少的有关文件上传状态的信息。解决这个问题的另外一种尝试是实现一个applet——它通过FTP把文件上传到服务器。这一方案的缺点是:限制了你的用户,必须要有一个支持Java的浏览器。
在本文中,我们将实现一个具有AJAX能力的组件——它不仅实现把文件上传到服务器,而且"实时地"监视文件上传的实际过程。这个组件工作的四个阶段显示于下面的图1,2,3和4中:
图1.阶段1:选择文件上传 图2.阶段2:上传该文件到服务器 图3.阶段3:上传完成 图4.阶段4:文件上传摘要 |
public class UploadMultipartFilter implements Filter{ public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain) throws IOException, ServletException { HttpServletRequest hRequest = (HttpServletRequest)request; //检查是否我们在处理一个多部分请求 String contentHeader = hRequest.getHeader("content-type"); boolean isMultipart = ( contentHeader != null && contentHeader.indexOf("multipart/form-data") != -1); if(isMultipart == false){ chain.doFilter(request,response); }else{ UploadMultipartRequestWrapper wrapper = new UploadMultipartRequestWrapper(hRequest); chain.doFilter(wrapper,response); } ... } |
public class UploadMultipartRequestWrapper extends HttpServletRequestWrapper{ private Map<String,String> formParameters; private Map<String,FileItem> fileParameters; public UploadMultipartRequestWrapper(HttpServletRequest request) { super(request); try{ ServletFileUpload upload = new ServletFileUpload(); upload.setFileItemFactory(new ProgressMonitorFileItemFactory(request)); List fileItems = upload.parseRequest(request); formParameters = new HashMap<String,String>(); fileParameters = new HashMap<String,FileItem>(); for(int i=0;i<fileItems.size();i++){ FileItem item = (FileItem)fileItems.get(i); if(item.isFormField() == true){ formParameters.put(item.getFieldName(),item.getString()); }else{ fileParameters.put(item.getFieldName(),item); request.setAttribute(item.getFieldName(),item); } } }catch(FileUploadException fe){ //请求时间超过-用户可能已经转到另一个页面。 //作一些记录 //... } ... |
public class ProgressMonitorFileItemFactory extends DiskFileItemFactory { private File temporaryDirectory; private HttpServletRequest requestRef; private long requestLength; public ProgressMonitorFileItemFactory(HttpServletRequest request) { super(); temporaryDirectory = (File)request.getSession().getServletContext().getAttribute("javax.servlet.context.tempdir"); requestRef = request; String contentLength = request.getHeader("content-length"); if(contentLength != null){requestLength = Long.parseLong(contentLength.trim());} } public FileItem createItem(String fieldName, String contentType,boolean isFormField, String fileName) { SessionUpdatingProgressObserver observer = null; if(isFormField == false) //这必须是一文件上传. observer = new SessionUpdatingProgressObserver(fieldName,fileName); ProgressMonitorFileItem item = new ProgressMonitorFileItem( fieldName,contentType,isFormField, fileName,2048,temporaryDirectory, observer,requestLength); return item; } ... public class SessionUpdatingProgressObserver implements ProgressObserver { private String fieldName; private String fileName; ... public void setProgress(double progress) { if(request != null){ request.getSession().setAttribute("FileUpload.Progress."+fieldName,progress); request.getSession().setAttribute("FileUpload.FileName."+fieldName,fileName); } } } } |