一:方案分析
基于浏览器的文件上传,为了有更好的用户体验,我们一般就设置一个旋转的图标,旋转的图标无法实时的监控文件上传情况。所以我们将实现一个如何实时的监控文件的上传。
参考资料:http://www.cnblogs.com/ybase/archive/2011/11/15/2249298.html
技术问题分析:
- 如何实现上传的功能
- 如何实现上传文件的监听功能
- 如何实现记录上传状态的功能
- 客户端如何状态的实时显示
解决方案:
- 如何实现上传的功能 ------使用apache的FileUpload组件上传文件
- 如何实现上传文件的监听功能 ------使用ProgressListener监听文件状态
- 如何实现记录上传状态的功能 ------使用session保存文件的状态
- 客户端如何状态的实时显示 -------客户端使用AJAX来查询上传的状态
基本思路:
客户端:界面的提交的时候使用<iframe 来实现模拟的无刷新提交,然后在使用ajax来周期的访问servlet并返回sesson中最新的状态信息。
服务器端:在servlet介绍到请求的时候,区分请求的类型是上传的请求还是ajax询问的请求,如果是上传的请求,则执行上传的方法,并启动监听保存上传状态到session中。
在介绍源代码之前,我们先来看看程序运行界面:
接下来是源文件的目录结构:
二、实现代码
UploadFileProgressBar.java ------使用apache的FileUpload组件上传文件
- package com.yangpan.upload.progressbar;
-
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.io.PrintWriter;
- import java.text.SimpleDateFormat;
- import java.util.Arrays;
- import java.util.Date;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Random;
-
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- 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.FileUploadException;
- import org.apache.commons.fileupload.ProgressListener;
- import org.apache.commons.fileupload.disk.DiskFileItemFactory;
- import org.apache.commons.fileupload.servlet.ServletFileUpload;
- /**
- * 有进度条的上传
- *
- * @author 妞见妞爱
- *
- */
- public class UploadFileProgressBar extends HttpServlet {
-
- /**
- *
- */
- private static final long serialVersionUID = 1L;
- //定义允许上传的文件扩展名
- protected HashMap<String, String> extMap = new HashMap<String, String>();
- //最大文件大小 100 M --测试用
- protected long maxSize = 100 * 1024 * 1024;
- //上传文件的保存路径
- protected String configPath = "attached/";
-
- protected String dirTemp = "attached/temp/";
-
- protected String dirName = "file";
-
- public void init() throws ServletException {
-
- //定义允许上传的文件扩展名
- //extMap.put("image", "gif,jpg,jpeg,png,bmp");
- //extMap.put("flash", "swf,flv");
- //extMap.put("media", "swf,flv,mp3,wav,wma,wmv,mid,avi,mpg,asf,rm,rmvb");
- extMap.put("file", "doc,docx,xls,xlsx,ppt,htm,html,txt,zip,rar");
-
-
-
- }
-
-
- /**
- * 处理上传文件
- * @param request
- * @param response
- * @throws ServletException
- * @throws IOException
- */
- @SuppressWarnings("unchecked")
- public void processFileUpload(HttpServletRequest request, PrintWriter out)
- throws ServletException, IOException {
-
- //文件保存目录路径
- String savePath = this.getServletContext().getRealPath("/") + configPath;
-
- // 临时文件目录
- String tempPath = this.getServletContext().getRealPath("/") + dirTemp;
-
- SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM");
- String ymd = sdf.format(new Date());
- savePath += "/" + ymd + "/";
- //创建文件夹
- File dirFile = new File(savePath);
- if (!dirFile.exists()) {
- dirFile.mkdirs();
- }
-
- tempPath += "/" + ymd + "/";
- //创建临时文件夹
- File dirTempFile = new File(tempPath);
- if (!dirTempFile.exists()) {
- dirTempFile.mkdirs();
- }
-
- DiskFileItemFactory factory = new DiskFileItemFactory();
- factory.setSizeThreshold(20 * 1024 * 1024); //设定使用内存超过5M时,将产生临时文件并存储于临时目录中。
- factory.setRepository(new File(tempPath)); //设定存储临时文件的目录。
-
- ServletFileUpload upload = new ServletFileUpload(factory);
- upload.setHeaderEncoding("UTF-8");
-
- //创建一个进度监听器
- FileUploadProgressListener progressListener = new FileUploadProgressListener(request);
- upload.setProgressListener(progressListener);
-
- try {
- List items = upload.parseRequest(request);
- Iterator itr = items.iterator();
- while (itr.hasNext()) {
- FileItem item = (FileItem) itr.next();
- String fileName = item.getName();
- long fileSize = item.getSize();
- if (!item.isFormField()) {
- //检查文件大小
- if(item.getSize() > maxSize){
- setStatusMsg(request, "1", "上传文件大小超过限制。");
- return;
- }
- //检查扩展名
- String fileExt = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
- if(!Arrays.<String>asList(extMap.get(dirName).split(",")).contains(fileExt)){
- setStatusMsg(request, "1", "上传文件扩展名是不允许的扩展名。只允许" + extMap.get(dirName) + "格式。");
- return;
- }
-
- SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
- String newFileName = df.format(new Date()) + "_" + new Random().nextInt(1000) + "." + fileExt;
- try{
- File uploadedFile = new File(savePath, newFileName);
-
- /*
- * 第一种方法
- *
- * 好处: 一目了然..简单啊...
- * 弊端: 这种方法会导致上传的文件大小比原来的文件要大
- *
- * 推荐使用第二种
- */
- //item.write(uploadedFile);
- //--------------------------------------------------------------------
- //第二种方法
- OutputStream os = new FileOutputStream(uploadedFile);
- InputStream is = item.getInputStream();
- byte buf[] = new byte[1024];//可以修改 1024 以提高读取速度
- int length = 0;
- while( (length = is.read(buf)) > 0 ){
- os.write(buf, 0, length);
- }
- //关闭流
- os.flush();
- os.close();
- is.close();
-
- }catch(Exception e){
- setStatusMsg(request, "1", "上传文件失败。");
- return;
- }
- setStatusMsg(request, "2", "文件上传成功!");
- }
- }
-
- } catch (FileUploadException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
-
- }
-
- /**
- *
- * 错误信息的处理
- *
- * @param request
- * @param error -- 1 : 错误 0 : 正常 2 : 上传完成
- * @param message
- */
- private void setStatusMsg(HttpServletRequest request, String error, String message) {
- HttpSession session = request.getSession();
- FileUploadStatus status = (FileUploadStatus) session.getAttribute("upladeStatus");
- status.setError(error);
- status.setStatusMsg(message);
- }
-
- /**
- *
- * 获取状态信息
- *
- * @param request
- * @param out
- */
- @SuppressWarnings("unused")
- private void getStatusMsg(HttpServletRequest request,PrintWriter out){
- HttpSession session = request.getSession();
- FileUploadStatus status = (FileUploadStatus) session.getAttribute("upladeStatus");
- System.out.println("输出信息对象:"+status);
- out.println(status.toJSon());
- }
-
-
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
-
- request.setCharacterEncoding("UTF-8");
- response.setContentType("text/html; charset=UTF-8");
-
- PrintWriter out = response.getWriter();
- //检查输入请求是否为multipart表单数据。
- boolean isMultipart= FileUpload.isMultipartContent(request);
- if (isMultipart) {
- processFileUpload(request, out);
- }else {
- if (request.getParameter("uploadStatus") != null) {
- //response.setContentType("text/xml");
- //response.setHeader("Cache-Control", "no-cache");
- System.out.println("ajax 读取状态····");
- getStatusMsg(request, out);
- }
- }
-
- out.flush();
- out.close();
- }
-
-
-
- }
FileUploadProgressListener------使用ProgressListener监听文件状态
- package com.yangpan.upload.progressbar;
-
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpSession;
-
- import org.apache.commons.fileupload.ProgressListener;
-
- /**
- * 上传监听器
- *
- * @author 妞见妞爱
- *
- */
- public class FileUploadProgressListener implements ProgressListener {
-
- private HttpSession session;
- private long megaBytes = -1;
-
-
- public FileUploadProgressListener(HttpServletRequest request) {
- session = request.getSession();
- FileUploadStatus newUploadStatus = new FileUploadStatus();
- session.setAttribute("upladeStatus", newUploadStatus);
- }
-
- /**
- *
- * 为了进度条监听器不会引起性能问题
- * 解决方案,是减少进步条的活动数
- * 比如,只有当上传了1兆字节的时候才反馈给用户
- *
- */
- public void update(long pBytesRead, long pContentLength, int pItems) {
- /*long mBytes = pBytesRead / 1048576;
- if (megaBytes == mBytes) {
- return;
- }
- megaBytes = mBytes;*/
- FileUploadStatus status = (FileUploadStatus) session.getAttribute("upladeStatus");
- if (pContentLength == -1) {
- status.setStatusMsg("已完成" + pItems +"个文件的上传");
- }else {
- status.setStatusMsg("正在上传第" + pItems +"个文件");
- }
- status.setError("0");
- status.setReadedBytes(pBytesRead);
- status.setTotalBytes(pContentLength);
- status.setCurrentItem(pItems);
- }
-
- }
FileUploadStatus------使用session保存文件的状态
- package com.yangpan.upload.progressbar;
-
-
- /**
- *
- * 上传状态类
- *
- * @author 妞见妞爱
- *
- */
- public class FileUploadStatus {
-
- private String statusMsg = "";
- private long readedBytes = 0L;
- private long totalBytes = 0L;
- private int currentItem = 0;
- // 1 : 错误 0 : 正常 2:完成
- private String error = "0";
-
- public String getStatusMsg() {
- return statusMsg;
- }
-
- public void setStatusMsg(String statusMsg) {
- this.statusMsg = statusMsg;
- }
-
- public long getReadedBytes() {
- return readedBytes;
- }
-
- public void setReadedBytes(long readedBytes) {
- this.readedBytes = readedBytes;
- }
-
- public long getTotalBytes() {
- return totalBytes;
- }
-
- public void setTotalBytes(long totalBytes) {
- this.totalBytes = totalBytes;
- }
-
- public int getCurrentItem() {
- return currentItem;
- }
-
- public void setCurrentItem(int currentItem) {
- this.currentItem = currentItem;
- }
-
- public String getError() {
- return error;
- }
-
- public void setError(String error) {
- this.error = error;
- }
-
- public String toJSon() {
- StringBuffer strJSon = new StringBuffer();
- strJSon.append("{");
- strJSon.append("error:'").append(error).append("',");
- strJSon.append("statusMsg:'").append(statusMsg).append("',");
- strJSon.append("readedBytes:'").append(readedBytes).append("',");
- strJSon.append("totalBytes:'").append(totalBytes).append("',");
- strJSon.append("currentItem:'").append(currentItem).append("'");
- strJSon.append("}");
- return strJSon.toString();
- }
- }
web.xml
- <servlet>
- <servlet-name>UploadFileProgressBar</servlet-name>
- <servlet-class>
- com.yangpan.upload.progressbar.UploadFileProgressBar
- </servlet-class>
- </servlet>
-
- <servlet-mapping>
- <servlet-name>UploadFileProgressBar</servlet-name>
- <url-pattern>/servlet/UploadFileProgressBar</url-pattern>
- </servlet-mapping>
客户端代码:
三、总结
疑惑一:
监听器不会引起性能问题,因为它是实时的。我看有些文档上面有说可能会。我暂时把它注释掉了。
扩展:
1、上传成功后。点击删除按钮删除上传文件---这个倒简单
2、上传中。点击取消上传,---这个如何实现? 希望大家讨论下...是不是可以直接关闭什么输入输出流来实现?
O(∩_∩)O哈哈~..差不多了...说的差不多了,希望它多少能为您的工作或学习带来点儿帮助。
|