1,首先我们要熟悉apache的commons-fileUpload,不熟悉的同学,可以到http://commons.apache.org/fileupload/using.html和http://commons.apache.org/fileupload/apidocs/index.html里浏览一番;
2,可以利用Hibernate的Pojo思想进行设计,既然在客户端的目标要显示文件上传的进度,那么就必须得组织一人这样的类,进行提供服务,为了方便管理,利用组合和面向接口编程的习惯我们建立一个uploadInfo管理者uploadInofManger这两个类都是带有接口,同时uploadInfoManager组合了uploadInfo请看我已经实现的代码:
uploadInfoI,这个是反馈对象的接口
package uploadFileCommon; /** *uploadInof类的接口,对应于直接拥有uploadInfo类的责任 * */ public interface UploadInfoI { /** * 得到文件总大小 MKB * * @return */ public abstract float getTotalSizeMKB(); /** * 得到已上传大小 MKB * * @return */ public abstract float getUploadSizeMKB(); /** * 得到文件名 * * @return */ public abstract String getFileName(); /** * 得到完成的比例参数 * * @return */ public abstract int getCompletePercent(); /** * 得到总共需要时间 H M S * * @return */ public abstract String getTotalTimeHMS(); /** * 得到余下时间 H M S * * @return */ public abstract String getRemainTimeHMS(); /** * 得到上传速度 KB * * @return */ public abstract float getUploadSpeedKB(); /** * 重新设置参数,用户选择重新上传时用,这个方法目标是为了在commons-fileupload的监听器了重用 * uploadInfo对象,这样就不会频繁创建uploadInfo */ public abstract void reload(); }
UploadInfoImpl这个是反馈对象的实现类:
package uploadFileCommon; /** * 上传进度反馈对象 * * @author Administrator * */ public class UploadInfoImpl implements UploadInfoI { /** * 针对哪个文件的反馈信息对象标识 */ private String fileIndex; /** * 文件名 */ private String fileName = null; /** * 开始时间长度 */ private long beginTime = 0L; /** * 文件总大小 */ private long totalSize = 0L; /** * 上传时间,此时间是在开始时间后递加 */ private long uploadTime = 0L; /** * 已上传大小 */ private long uploadSize = 0L; private String status = "done"; public UploadInfoImpl() { super(); // TODO Auto-generated constructor stub } public UploadInfoImpl(String fileName, long beginTime, long totalSize, long uploadTime, long uploadSize) { super(); this.fileName = fileName; this.beginTime = beginTime; this.totalSize = totalSize; this.uploadTime = uploadTime; this.uploadSize = uploadSize; } /** * * @param fileIndex 索引号字符串 * @param fileName 文件名 * @param beginTime 开始监听时间点 * @param uploadTime 已上传时间 * @param uploadSize 已上传大小 * @param totalSize 总共大小 */ public UploadInfoImpl(String fileIndex, String fileName, long beginTime, long uploadTime, long uploadSize, long totalSize) { super(); this.fileIndex = fileIndex; this.fileName = fileName; this.beginTime = beginTime; this.totalSize = totalSize; this.uploadTime = uploadTime; this.uploadSize = uploadSize; } public int getCompletePercent() { int completePercent = (int) (((float) uploadSize / (float) totalSize) * 100F); return completePercent; } public float getRemainTime() { float remainTime = (float) (totalSize - uploadSize) / (getUploadSpeedKB() * 1000F); return (float) Math.round(remainTime * 100F) / 100F; } public String getRemainTimeHMS() { int remainS = (int) getRemainTime(); int remainM = remainS / 60; int remainH = remainM / 60; remainM -= remainH * 60; remainS = remainS - remainH * 3600 - remainM * 60; return remainH + ":" + remainH + ":" + remainS; } public float getTotalSizeMKB() { float kbTotalSize = (float) totalSize / 1000000F; return (float) Math.round(kbTotalSize * 100F) / 100F; } public float getTotalTime() { float totalTimeS = (float) totalSize / (getUploadSpeedKB() * 1000F); return (float) Math.round(totalTimeS * 100F) / 100F; } public String getTotalTimeHMS() { int totalS = (int) getTotalTime(); int totalM = totalS / 60; int totalH = totalM / 60; totalM -= totalH * 60; totalS = totalS - totalH * 3600 - totalM * 60; return totalH + ":" + totalM + ":" + totalS; } public float getUploadSizeMKB() { float kbByteTotalSize = (float) uploadSize / 1000000F; return (float) Math.round(kbByteTotalSize * 100F) / 100F; } public float getUploadSpeedKB() { float kbPerS = (float) uploadSize / (float) (uploadTime - beginTime); return (float) Math.round(kbPerS * 100F) / 100F; } public String toString(UploadInfoI preItem) { return "file:" + getFileName() + "|completePercent:" + getCompletePercent() + "%|uploadSize:" + getUploadSizeMKB() + " kb|totalSize:" + getTotalSizeMKB() + "kb|uploadSpeed:" + getUploadSpeedKB() + "kb/s|remainTime:" + getRemainTime() + "s|totalTime:" + getTotalTime() + "s"; } public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = fileName; } public long getBeginTime() { return beginTime; } public void setBeginTime(long beginTime) { this.beginTime = beginTime; } public long getTotalSize() { return totalSize; } public void setTotalSize(long totalSize) { this.totalSize = totalSize; } public long getUploadTime() { return uploadTime; } public void setUploadTime(long uploadTime) { this.uploadTime = uploadTime; } public long getUploadSize() { return uploadSize; } public void setUploadSize(long uploadSize) { this.uploadSize = uploadSize; } public String getFileIndex() { return fileIndex; } public void setFileIndex(String fileIndex) { this.fileIndex = fileIndex; } @Override public void reload() { fileName = null; beginTime = 0L; totalSize = 0L; uploadSize = 0L; uploadTime = 0L; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } }
好了下面,要做的就是创建反馈对象uploadInfo的管理者,那么它应该怎样管理uploadInfo呢,就用接口说明
UploadInfoManagerI:
package uploadFileCommon; public interface UploadInfoManagerI { public abstract void init(); /** * 字符串形式返回上传的百分比 * * @return */ public abstract String getCompletePercentStr(); /** * 字符串形式返回上传速度 * * @return */ public abstract String getUploadSpeek(); /** * 字符串形式返回剩余时间 * @return */ public abstract String getRemainTime(); }
由于为了可能的重用uploadInfo所以在它的的实现类了会增加一个获取当前uploadInfo的服务
UploadInfoManagerImpl:
package uploadFileCommon; /** * UploadInfoI管理类,既要向监听器提供看是否可重用的UploadInfo,又要向外界提供进度消息 * * @author Administrator * */ public class UploadInfoMangerImpl implements UploadInfoManagerI { private static UploadInfoI uploadInfo = null; public UploadInfoMangerImpl() { super(); } @Override public void init() { } @Override public String getCompletePercentStr() { if (uploadInfo != null) { return String.valueOf(uploadInfo.getCompletePercent()); } else { return null; } } @Override public String getUploadSpeek() { if (uploadInfo != null) { return String.valueOf(uploadInfo.getUploadSpeedKB()); } else { return null; } } public String getRemainTime() { if (uploadInfo != null) { return uploadInfo.getRemainTimeHMS(); } else { return null; } } public static UploadInfoI getUploadInfo() { return uploadInfo; } public static void setUploadInfo(UploadInfoI uploadInfo) { UploadInfoMangerImpl.uploadInfo = uploadInfo; } public String getTotalTime() { return uploadInfo.getTotalTimeHMS(); } }
下面关键的类来了,就是真正处理request进而包含的要上传文件
特点:根据资源文件的配置来自定义对上传文件的要求,包含类型,大小,内存缓存,临时磁盘缓冲等等,它通过解析request识别它里面的文件项,和非文件项,以list集合形式返回,我写的里面特别注意是它需要有一个工厂类(DiskFileItemFactory)对初始化出来,当然很多设置我们会看到是在初始化它的工厂对象里配置的
package uploadFileCommon; import java.io.File; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.ResourceBundle; import javax.servlet.http.HttpServletRequest; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.FileCleanerCleanup; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.io.FileCleaningTracker; public class FileUpload { private static final long K = 1024; private static final long M = 1024 * 1024; public static ResourceBundle config = ResourceBundle.getBundle("config");// 外部配置文件 private long sizeMax = getByteSize(config.getString("onceMaxSize"));// 一次post最大的数据量 private long fileLimitSize = getByteSize(config.getString("fileLimitSize"));// 默认的单个文件大小限制,0为无限制,配置文件中存放的为String类型 private String allowFileTypes = config.getString("allowFileTypes"); // 合法的上传类型 private long sizeThreshold = getByteSize(config.getString("cacheSize"));// cacheSize private String tempSavePath = config.getString("tempSavePath"); private List invalidFiles = new ArrayList(); // 不合法的文件 // file upload Handler private ServletFileUpload fileUploadHandler = null; // fileItemFactory 往里面设置参数进行控制上传文件项 private DiskFileItemFactory fileItemFactory = null; public FileUpload(HttpServletRequest request) { super(); FileCleaningTracker fileCleaningTracker = FileCleanerCleanup .getFileCleaningTracker(request.getSession() .getServletContext()); this.fileItemFactory = new DiskFileItemFactory(); this.fileItemFactory.setFileCleaningTracker(fileCleaningTracker); this.fileItemFactory.setSizeThreshold((int) sizeThreshold); this.fileItemFactory.setRepository(new File(this.tempSavePath));// 如果文件超过大小则缓存在这里 this.fileUploadHandler = new ServletFileUpload(this.fileItemFactory); this.fileUploadHandler.setSizeMax(this.sizeMax); // 设置允许上传的最大文件 this.fileUploadHandler.setProgressListener(new FileUploadListener( request)); } /** * 解析request得到Item List 这个List包含非上传的项目 * * @param request * @return */ public List parseRequest(HttpServletRequest request) { List fileItem = null; try { fileItem = fileUploadHandler.parseRequest(request); } catch (FileUploadException e) { e.printStackTrace(); } return fileItem; } /** * 获得需要上传文件Item * * @return * @throws SizeLimitExceededException * @throws InvalidFileUploadException */ public List getUploadItem(HttpServletRequest request) throws SizeLimitExceededException, InvalidFileUploadException { List items = parseRequest(request); Iterator iter = items.iterator(); List uploadItem = new ArrayList(); String uploadItemName; String uploadItemType; long uploadItemSize; while (iter.hasNext()) { FileItem item = (FileItem) iter.next(); if (!item.isFormField()) { uploadItemName = item.getName(); uploadItemSize = item.getSize(); uploadItemType = item.getContentType(); if (!isAllowFileType(uploadItemType)) { invalidFiles.add(uploadItemName); throw new InvalidFileUploadException("非法的文件上传类型 [ " + uploadItemType + " ]", invalidFiles); } if (uploadItemSize > fileLimitSize) { invalidFiles.add(uploadItemName); throw new InvalidFileUploadException("[ " + uploadItemName + " ]超过单个文件大小限制,文件大小[ " + uploadItemSize + " ],限制为[ " + this.fileLimitSize + " ] ", invalidFiles); } System.out.println("上传文件类型为: " + uploadItemType + " " + "名字为: " + uploadItemName + " " + "大小为: " + uploadItemSize); uploadItem.add(item); } } System.out.println("upload item个数 : " + uploadItem.size()); return uploadItem; } /** * 根据传过来的大小长度单位,统一转化为lonh为单位的长度 */ public long getByteSize(String size) { String unit = size.substring(size.length() - 1).toUpperCase(); String num; if (unit.equals("K")) { num = size.substring(0, size.length() - 1); return Long.parseLong(num) * FileUpload.K; } else if (unit.equals("M")) { num = size.substring(0, size.length() - 1); return Long.parseLong(num) * FileUpload.M; } else { return Long.parseLong(size); } } /** * 判断是不是合法上传类型 * * @param fileType * @return */ private boolean isAllowFileType(String fileType) { if (allowFileTypes.length() > 0 && fileType.trim().length() > 0) return allowFileTypes.indexOf(fileType.toLowerCase()) != -1; else return true; } }
在利用FileUpload解析request得到哪些是需要上传项目时当然就是把文件内容保存到服务器了,怎么保存,当然要把责任交给我们打造的一个类CustomerFileUpload:里面有个关键的方法就是:saveFileToServer().此类就是通过组合FileUpload类来提供服务的,当然它获得的特殊资源就是控制器给的request
CustomerFileUpload:
package uploadFileCommon; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Iterator; import java.util.List; import javax.servlet.http.HttpServletRequest; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException; /** * 根据用户上传的文件,把文件保存到服务器同时把文件路径保存到数据库<br> * <br> * ########<br> * 笔记ServletContext是一个应用程序与Web容器(tomcat之类)交互的接口,同时它也是管理应用程序资源的对象 * ,通过它可以读取web.xml中一些配置,如 <context-param>中的信息,读取应用中的资源等。 * session是一个会话对象,也就是说当客户端访问服务器页面时,它才产生,原理是服务器对每个客户端产生一个sessionid值,并把它也保存在客户端, * 每次请求时验证这个sessionid是否是创建了sessionid的客户端 * ,在服务器端对应这个sessionid会创建一个缓存区,存储一些相关信息,程序员可以操作 * ,如被用于购物车,存储用户信息等,这个区域只有对特定用户有访问权限,对其他客户是拒绝访问的,这就是一个会话。 * request是一个请求对象,也就是说它的生命很短暂,只在一个请求中存在,如一个页面到另一个页面,一个页面到servlet或action,一个 * servlet或action到jsp页面等 * ,你可以用它的setAttribute方法传递需要的信息(对象),验证请求方的信息,获得请求方发过来的文件内容等。 * * @author Administrator * */ public class CustomerFileUpload { /** * 把此路径保存到DB */ private String fileRouteToDB; // 保存到数据库的文件路径 /** * 上传项目所属的用户 */ private String user = "1"; // 相应用户 /** * 解析的request */ private HttpServletRequest request; /** * 处理上传项目的对象 */ private static FileUpload httpFileUpload; /** * 保存到服务器的路径 */ private static String serverSavedRoute; /** * 需要upload的项目 */ private List uploadItem; public CustomerFileUpload() { super(); } public CustomerFileUpload(HttpServletRequest request) throws SizeLimitExceededException, InvalidFileUploadException { super(); this.request = request; this.httpFileUpload = new FileUpload(request); try { serverSavedRoute = new File(".").getCanonicalPath() + File.separator + "upload" + File.separator; } catch (IOException e1) { e1.printStackTrace(); } uploadItem = httpFileUpload.getUploadItem(request); } /** * 把需上传的数据保存到server * * @throws IOException * @throws InterruptedException */ public void saveFileToServer() throws IOException, InterruptedException { Iterator iter = uploadItem.iterator(); int value; while (iter.hasNext()) { FileItem uploadItem = (FileItem) iter.next(); BufferedInputStream input = new BufferedInputStream(uploadItem .getInputStream()); System.out.println(serverSavedRoute + uploadItem.getName() + " : 路径名字"); OutputStream outStreamToServer = new FileOutputStream( serverSavedRoute + uploadItem.getName()); BufferedOutputStream output = new BufferedOutputStream( outStreamToServer); while ((value = input.read()) != -1) { output.write(value); } input.close(); outStreamToServer.close(); output.close(); } } public String getFileRouteToDB() { return fileRouteToDB; } public String getUser() { return user; } public void setUser(String user) { this.user = user; } public HttpServletRequest getRequest() { return request; } public void setRequest(HttpServletRequest request) { this.request = request; } public static FileUpload getHttpFileUpload() { return httpFileUpload; } public static void setHttpFileUpload(FileUpload httpFileUpload) { CustomerFileUpload.httpFileUpload = httpFileUpload; } public String getServerSavedRoute() { return serverSavedRoute; } public void setServerSavedRoute(String serverSavedRoute) { this.serverSavedRoute = serverSavedRoute; } }
现在好问题来了,怎么没见到产生进度信息的对象在哪里产生,下面就给出,commons-fileupload已经为我们想好了产生进度反馈信息的场所就是FileUpload的监听器,它通过监听servletContext不断触发一个接口的方法产生进度信息,这个方法就是在接口ProgressListener 里,所有上面写FileUpload时肯定有一句是注入监听器的代码:
FileUploadListener:
package uploadFileCommon; import javax.servlet.http.HttpServletRequest; import org.apache.commons.fileupload.ProgressListener; public class FileUploadListener implements ProgressListener { private UploadInfoI uploadInfo; private long megaBytes = -1; /** *开始监听一次时间点 */ private long beginListenerTime; public FileUploadListener(HttpServletRequest request) { super(); this.beginListenerTime = System.currentTimeMillis(); } @Override public void update(long bytesRead, long contentLength, int items) { long mBytes = bytesRead / 1000000; // 这样bytesRead的值每次超过1000000 // mBytes的值就加1 if (megaBytes == mBytes) { return; } megaBytes = mBytes; String itemId = new Integer(items).toString(); String fileName = itemId + "号文件"; String fileIndex = String.valueOf(items); long beginTime = this.beginListenerTime; long totalSize = contentLength; long uploadTime = System.currentTimeMillis();// - long uploadSize = bytesRead; this.uploadInfo = UploadInfoMangerImpl.getUploadInfo(); if (this.uploadInfo == null) { this.uploadInfo = UploadInfoFactory.create(fileIndex, fileName, beginTime, totalSize, uploadTime, uploadSize, uploadInfo); UploadInfoMangerImpl.setUploadInfo(uploadInfo); } UploadInfoImpl uploadInfoImpl = ((UploadInfoImpl) this.uploadInfo); uploadInfoImpl.setFileIndex(fileIndex); uploadInfoImpl.setBeginTime(beginTime); uploadInfoImpl.setFileName(fileName); uploadInfoImpl.setTotalSize(totalSize); uploadInfoImpl.setUploadTime(uploadTime); uploadInfoImpl.setUploadSize(uploadSize); System.out.println(uploadInfoImpl.getUploadSpeedKB() + "KB 上传速度..."); System.out.println(uploadInfoImpl.getRemainTimeHMS() + "H:M:S 剩余时间..."); System.out.println(uploadInfoImpl.getCompletePercent() + "%完成的百分比..."); System.out.println(uploadInfoImpl.getTotalTimeHMS() + "H:M:S总共用时..."); } public UploadInfoI getUploadInfo() { return uploadInfo; } public void setUploadInfo(UploadInfoI uploadInfo) { this.uploadInfo = uploadInfo; } public long getMegaBytes() { return megaBytes; } public void setMegaBytes(long megaBytes) { this.megaBytes = megaBytes; } public long getBeginListenerTime() { return beginListenerTime; } public void setBeginListenerTime(long beginListenerTime) { this.beginListenerTime = beginListenerTime; } }
FileUploadListener里面的update()方法里利用了工厂模式来提供uploadInfo,上的是为了每次监听到内容都要new 一个uploadInfo,还有一个是控制监听处理工作的时间点的计算,这个认真看完http://commons.apache.org/fileupload/using.html的人没有难度知道原理和实现:
下面就是控制器,一个servlet
package uploadFileCommon; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException; /** * 利用Common-uploadFile组件进行控制文件上传 * * @author Administrator * */ public class FileUploadServlet extends HttpServlet { private static final long serialVersionUID = 1L; private CustomerFileUpload customerSaveFile = null; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //解决此组件上传中文文件名的乱码 request.setCharacterEncoding("UTF-8"); try { this.customerSaveFile = new CustomerFileUpload(request); this.customerSaveFile.saveFileToServer(); } catch (SizeLimitExceededException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvalidFileUploadException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } request.getRequestDispatcher("/index.jsp").forward(request, response); } public CustomerFileUpload getCustomerSaveFile() { return customerSaveFile; } public void setCustomerSaveFile(CustomerFileUpload customerSaveFile) { this.customerSaveFile = customerSaveFile; } }
后台的过程完成了一大半,只要用dwr把不断监听到的内容显示就得了,我直接用dwr把反馈对象的管理者uploadInfoMangerImpl暴露出来:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GB2312"> <title>Upload Page</title> <script type="text/javascript" src="dwr/util.js"></script> <script type="text/javascript" src="dwr/engine.js"></script> <script type="text/javascript" src="dwr/interface/uploadInfoService.js"></script> <script type="text/javascript"> timer = window.setInterval("showUploadInfo()",700); function showUploadInfo(){ uploadInfoService.getCompletePercentStr(function(data){ $('uploadPercent').value="已上传: " + data + " %"; }); uploadInfoService.getUploadSpeek(function(data){ $('uploadSpeed').value="速度: " + data + " KB/S"; }); uploadInfoService.getRemainTime(function(data){ $('uploadRemainTime').value="剩余: " + data + " H:M:S"; }); } </script> </head> <body> <form name="uploadForm" enctype="multipart/form-data" action="http://localhost:8080/basicjava/upload" method="POST"> <input type="text" name="hello" /> 上载后文件名称: <input name="name1" type="text" /> <br> 选择文件: <input name="contents1" type="file" /> <br> 上载后文件名称: <input name="name1" type="text" /> <br> 选择文件: <input name="contents1" type="file" /><br> <label>上传进度:</label> <input type="text" id="uploadPercent" name="uploadPercent"><br> <label>上传速度:</label> <input type="text" id="uploadSpeed" name="uploadSpeed"><br> <label>上传剩余时间:</label> <input type="text" id="uploadRemainTime" name="uploadRemainTime"><br> <input type="submit" value="Submit" /> </form> </body> </html>
dwr配置也很简单,里面有一个文章是说它的,其实就是一个客户端的定时器在不断向服务器请求反馈对象的进度信息.
写完了,希望帮助一样爱学习,自学的同学..