servlet上传功能前端和后端的实现

来源:互联网 发布:苹果锁屏背单词软件 编辑:程序博客网 时间:2024/05/16 05:36

最近项目需要做一个文件上传功能,做完了分享下,顺带当做笔记。
上传功能用后台用java实现,前端主要是js的ajax实现。后台还加入定时删除临时文件。
效果如图
这里写图片描述
这里写图片描述
首先是上传功能的主要类,下面是代码

package util.upload;import java.io.File;import java.io.IOException;import java.text.SimpleDateFormat;import java.util.Date;import java.util.Iterator;import java.util.List;import java.util.UUID;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.disk.DiskFileItemFactory;import org.apache.commons.fileupload.servlet.ServletFileUpload;public class UploadServlet extends HttpServlet { private static final long serialVersionUID = -3100028422371321159L; private boolean isAllowed; private String upFileName;    //定义合法后缀名的数组 private String[] allowedExtName=new String[]            {"zip","rar",//压缩文件         "txt","doc","wps","docx","java",//文本         "xls","xlsx",//表格         "ppt","pptx",//幻灯片         "pdf",//pdf         "jpg","jpeg","bmp","gif","png"//图片         }; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {  doPost(request, response);  } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {  //设置编码格式为utf-8  request.setCharacterEncoding("utf-8");  response.setCharacterEncoding("utf-8");   //获取session,保存进度和上传结果,上传开始为nok,当为Ok表示上传完成  HttpSession session=request.getSession();  session.setAttribute("result", "nok");  session.setAttribute("error", "");  String error="";  upFileName="";  isAllowed=false;  //给上传的文件设一个最大值,这里是不得超过100MB  int maxSize=100*1024*1024;  //创建工厂对象和文件上传对象  DiskFileItemFactory factory=new DiskFileItemFactory();  ServletFileUpload upload=new ServletFileUpload(factory);  //创建上传监听器和设置监听器  UploadListener listener=new UploadListener();  session.setAttribute("LISTENER", listener);  upload.setProgressListener(listener);    //上传路径  String path = request.getSession().getServletContext().getRealPath("/upload");  String requestPath = request.getSession().getServletContext().getContextPath()+"/upload";  File dirFile =new File(path);     //System.out.println(request.getSession().getServletContext().getContextPath());  //如果文件夹不存在则创建      if  (!dirFile .exists()  && !dirFile .isDirectory())        {             dirFile .mkdir();      }      //根据日期创建文件夹,保存到对应日期的文件夹下  Date date=new Date();  SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMdd");  String subDirName=sdf.format(date);  File subDirFile=new File(path+"/"+subDirName);  if  (!subDirFile .exists()  && !subDirFile .isDirectory())        {             subDirFile .mkdir();      }     try {     //解析上传请求   List<FileItem> items=upload.parseRequest(request);      Iterator<FileItem> itr=items.iterator();      while(itr.hasNext()){          FileItem item=(FileItem)itr.next();    //判断是否为文件域       if(!item.isFormField()){     if(item.getName()!=null&&!item.getName().equals("")){      //获取上传文件大小和文件名称      long upFileSize=item.getSize();         String fileName=item.getName();      //获取文件后缀名      String[] splitName=fileName.split("\\.");      String extName=splitName[splitName.length-1];      //检查文件后缀名      for(String allowed:allowedExtName)      {        if(allowed.equalsIgnoreCase(extName))        {            isAllowed=true;        }             }      if(!isAllowed){          error="上传文件格式不合法!";          break;      }      if(upFileSize>maxSize){       error="您上传的文件太大了,请选择不超过100MB的文件!";       break;      }        //此时文件暂存在服务器的内存中,构造临时对象      File tempFile=new File(makeFileName(fileName));      //指定文件上传服务器的目录及文件名称      File file=new File(path+"/"+subDirName+"/",tempFile.getName());      item.write(file);//第一种写文件方法      upFileName=requestPath+"/"+subDirName+"/"+tempFile.getName();  if(upFileName.equals("")){      error="没选择上传文件!";  }  System.out.println(upFileName);      /*//构造输入流读文件 第二种写文件方法      InputStream is=item.getInputStream();      int length=0;      byte[] by=new byte[1024];      FileOutputStream fos=new FileOutputStream(file);      while((length=is.read(by))!=-1){       fos.write(by, 0, length);       //Thread.sleep(10);      }      fos.close();      //Thread.sleep(1000);*/     }else{      error="没选择上传文件!";     }    }   }      } catch (Exception e) {   e.printStackTrace();   error="上传文件出现错误:"+e.getMessage();  }  if(!error.equals("")){       System.out.println(error);   session.setAttribute("error", error);  }else{     session.setAttribute("result", "OK");    session.setAttribute("filename",upFileName);  } } /**  * 为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名  * @param filename 原文件名  * @return 生成的唯一文件名  */ private String makeFileName(String filename){      return UUID.randomUUID().toString() + "_" + filename;   } }

其中需要引入commons-fileupload-1.3.1.jar,commons-io-2.4.jar
上传过程中,我们需要实时获取上传进度等信息,引入的库里为我们添加了一个ProgressListener接口,我们再写一个类实现这个接口,上面类中添加该接口

//创建工厂对象和文件上传对象  DiskFileItemFactory factory=new DiskFileItemFactory();  ServletFileUpload upload=new ServletFileUpload(factory);  //创建上传监听器和设置监听器  UploadListener listener=new UploadListener();  session.setAttribute("LISTENER", listener);  upload.setProgressListener(listener);

下面是这个监听类的具体实现代码

package util.upload;import org.apache.commons.fileupload.ProgressListener;public class UploadListener implements ProgressListener{    private volatile long     bytesRead = 0L,//上传的字节数    contentLength = 0L,//总字节数    item = 0L;      public UploadListener()         {            super();        }     @Override     public void update(long aBytesRead, long aContentLength, int anItem) {        bytesRead = aBytesRead;         contentLength = aContentLength;         item = anItem;     }     public long getBytesRead()      {         return bytesRead;     }     public long getContentLength()      {         return contentLength;     }     public long getItem()      {         return item;     }}

现在能获取上传进度等信息了,但还需要一个servlet返回给前端,下面实现

package util.upload;import java.io.IOException;import java.io.PrintWriter;import java.util.HashMap;import java.util.Map;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.ProgressListener;import com.google.gson.Gson;/** 获取上传进度,上传路径,错误,上传结果等信息 */public class GetProgressServlet extends HttpServlet{ private static final long serialVersionUID = -3596466520775012991L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {  doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {      request.setCharacterEncoding("utf-8");      response.setCharacterEncoding("utf-8");      UploadListener listener= null;      HttpSession session = request.getSession();      String error=(String) session.getAttribute("error");      String result= (String) session.getAttribute("result");      String fileName=(String) session.getAttribute("filename");      PrintWriter out = response.getWriter();      long bytesRead = 0,contentLength = 0;       if (session != null)        {            listener = (UploadListener)session.getAttribute("LISTENER");            if (listener == null)            {                return;            }            else            {                               bytesRead = listener.getBytesRead();//上传的字节数                contentLength = listener.getContentLength();//总字节数            }            //自己定义的返回格式            String rp=bytesRead+","                    +contentLength+","                    +error+","                    +result+","                    +fileName;            //System.out.println(rp);            out.print(rp);            /*      //返回json格式数据            Map<String,Object> map=new HashMap<String,Object>();            map.put("bytesRead", bytesRead);            map.put("contentLength", contentLength);            map.put("error", error);            map.put("result", result);            map.put("fileName", fileName);            Gson gson=new Gson();            String json=gson.toJson(map);            out.print(json);*/            out.flush();            out.close();            } }}

后台上传的功能代码写完了,下面实现上传的前端,首先是html

<!DOCTYPE html>    <html>    <head>        <meta charset="utf-8" />        <script type="text/javascript" src="js/upfile.js" charset="utf-8"></script>        <link rel="stylesheet" type="text/css" href="css/upfile.css">    </head>    <body >        <a href="javascript:addOne()">添加</a>        <div id="target">            <input type="file" id="file" name="file" onchange="addfile(event)" multiple/>        </div>        <span id="test">0</span>    </body> </html>

界面比较简单,就一个添加的a标签,负责上传的input隐藏起来
css文件主要渲染的上传进度的显示

            #file {                display: none;            }            .pro{                width:500px;            }            .pborder {                position: relative;                width: 500px; /* 宽度 */                border: 1px solid #B1D632;                padding: 1px;            }            .drawpro {                width: 0px;                display: block;                position: relative;                background: #B1D632;                color: #333333;                height: 20px; /* 高度 */                line-height: 20px; /* 必须和高度一致,文本才能垂直居中 */            }            .pspan {                position: absolute;                width: 500px;                text-align: center;                font-weight: bold;            }

接着是前端的重点,js文件

//显示上传信息的htmlvar upfile_html = '<div class="pborder"><div class="drawpro">'        + '<span class="pspan">0%</span></div></div><span name="path"></span><img src="common/upload/images/del.png" style="float:right" width="20" height="20" name="del" onclick=abortUpload(this)>';var targetDIV_id = "target";//显示上传文件的目标div的IDvar httpXML = null;//发送上传请求的XMLHttpRequest对象var httpProgress = null;//发送请求进度信息的XMLHttpRequest对象var oldFileList = new Array();//修改时保存已有附件信息的列表var uplist = new Array();//保存上传文件的列表var f_input;//上传文件的input对象var flag = true;//是否可以上传下一个文件标志var uurl = "Upload";//上传文件的请求urlvar gurl = "getProgress";//获取上传进度信息的urlvar cancelFlag = 0;//取消标志var timer, waittimer;//定时器var nowID = 0;//正在上传文件的idvar ID = 0;//队列中最后一个文件的id/** * 文件对象 */function UploadFile(id, file) {    this.id = id;    this.file = file;    this.state = 0;    this.path = "";}/** * 初始化的方法 */window.onload = function init() {    f_input = document.getElementById("file");    var tdiv = document.getElementById(targetDIV_id);    var oldspan = tdiv.getElementsByTagName("SPAN");    for ( var i = 0; i < oldspan.length; i++) {        oldFileList.push(oldspan[i].getAttribute("name"));    }}/** * 选择一个文件上传 */function addOne() {    f_input.value = null;    f_input.click();}/** * 选中文件后将文件对象添加到队列,开始上传 *  */function addfile(evt) {    var f = f_input.files[0];    if (f != undefined) {        var uf = new UploadFile(ID, f);        uplist.push(uf);        var div = document.createElement("DIV");        div.setAttribute("id", "pro" + (ID));        div.setAttribute("class", "pro");        div.innerHTML = upfile_html;        var targetDiv = document.getElementById(targetDIV_id);        targetDiv.appendChild(div);        div.getElementsByTagName("SPAN")[1].innerHTML = "文件名:"                + uplist[ID].file.name;        waittimer = setInterval("upload()", 1000);        ID++;    }}/** * 将队列中的文件上传 */function upload() {    if (flag == true) {        if (uplist.length > 0) {            var uf;            for ( var i = 0; i < uplist.length; i++) {                if (uplist[i].state == 0) {                    uf = uplist[i];                    uplist[i].state = 1;                    break;                }            }            if (uf != undefined & uf != null) {                flag = false;                if (window.XMLHttpRequest) {                    httpUP = new XMLHttpRequest();                } else if (window.ActiveXObject) {                    httpUP = new ActiveXObject("Microsoft.XMLHTTP");                }                var formData = new FormData();                formData.append("file", uf.file);                httpUP.open("POST", uurl, true);              httpUP.upload.addEventListener('progress', uploadProgress, false);                httpUP.send(formData);                nowID = uf.id;                timer = setInterval("getP()", 50);            }        }    }}/** * 获取上传进度等信息 */function getP() {    if (window.XMLHttpRequest) {        httpProgress = new XMLHttpRequest();    } else if (window.ActiveXObject) {        httpProgress = new ActiveXObject("Microsoft.XMLHTTP");    }    httpProgress.onreadystatechange = onProgress;    httpProgress.open("post", gurl, true);    httpProgress.setRequestHeader("Content-type",            "application/x-www-form-urlencoded");    httpProgress.send("&timeStamp=" + (new Date()).getTime());}/** * 处理返回的上传信息,显示到界面 */function onProgress() {    if (httpProgress.readyState == 4 && httpProgress.status == 200) {        result = httpProgress.responseText;        var result = result.replace(/(^\s*)|(\s*$)/g, "");        var res = result.split(",");        var now = parseInt(res[0]);        var all = parseInt(res[1]);        var err = res[2];        var state = res[3];        var path = res[4];        var per = (now / all * 100).toFixed(2);        var prodiv = document.getElementById("pro" + nowID);        if (prodiv != null & prodiv != undefined) {            if (err != "" & err != null & err.length > 0) {                window.clearInterval(timer);                if (cancelFlag == 1) {                    err = "上传终止";                    cancelFlag = 0;                }                prodiv.getElementsByTagName("DIV")[0].style.display = "none";                prodiv.getElementsByTagName("SPAN")[1].innerHTML = err;                httpUP.abort();                flag = true;                uplist[nowID].state = 3;                return;            }            if (state == "OK") {                prodiv.getElementsByTagName("DIV")[0].style.display = "none";                var tmpf = uplist[nowID].file;                prodiv.getElementsByTagName("SPAN")[1].innerHTML = "文件名:"                        + tmpf.name;                window.clearInterval(timer);                flag = true;                uplist[nowID].state = 2;                uplist[nowID].path = path;                return;            }            prodiv.getElementsByTagName("DIV")[1].style.width = per * 5 + "px";            prodiv.getElementsByTagName("SPAN")[0].innerHTML = per + "%";        }    }}/** * 取消上传的方法 */function abortUpload(obj) {    var idStr = obj.parentNode.id;    var id = idStr.slice(3);    if (uplist[id].state == 1) {        httpUP.abort();        flag = true;        cancelFlag = 1;    } else {        uplist[id].state = 3;    }    document.getElementById(idStr).remove();}/** * 获取上传文件的路径 * @returns 格式化后字符串 */function getFileListStr() {    var str = "";    if (oldFileList.length > 0) {        for ( var i = 0; i < oldFileList.length; i++) {            if (oldFileList[i] != null & oldFileList[i] != ""                    & oldFileList[i] != undefined) {                str = str + oldFileList[i] + ",";            }        }    }    for ( var i = 0; i < uplist.length; i++) {        var f = uplist[i];        if (f.state == 2) {            str = str + f.path + ",";        }    }    return str;}/** * 移除修改时已有的旧附件 *  */function removeOld(btn) {    var num = btn.getAttribute("name");    oldFileList[num - 1] = null;    btn.parentNode.remove();}   function uploadProgress(e) {                if (e.lengthComputable) {                    var iBytesUploaded = e.loaded;                    var iBytesTotal = e.total;                    document.getElementById("test").innerHTML=iBytesUploaded+"/"+iBytesTotal;                }            }

使用ajax发送上传文件,获取上传进度,结果等信息。
使用的html5的file API,所以必须ie9以上的才可以兼容,火狐还有个问题,ajax请求不立即返回,直到所有ajax请求都发送完了,才都返回同一个结果,这就导致上传进度不显示。不过上传进度信息也可以使用html5的file api获取,其中加了一点代码,页面下面test的div里的数值就是在前端获取到的进度。

上传的都实现完了,接着是处理上传后的临时文件,因为使用的uuid命名文件,所以文件会生成很多,没用的需要定时处理。使用ServletContextListener:
在 Servlet API 中有一个 ServletContextListener 接口,它能够监听 ServletContext 对象的生命周期,实际上就是监听 Web 应用的生命周期。

当Servlet 容器启动或终止Web 应用时,会触发ServletContextEvent 事件,该事件由ServletContextListener 来处理。在 ServletContextListener 接口中定义了处理ServletContextEvent 事件的两个方法。
利用其特性,实现定时删除临时文件的功能,代码如下:

package util.upload;import java.io.IOException;import java.io.InputStream;import java.util.Date;import java.util.Properties;import java.util.Timer;import java.util.TimerTask;import javax.servlet.ServletContext;import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;/**   * 时间监听器   *    *   */  public class TempFileListener implements ServletContextListener {      private Timer timer;      private SystemTaskTest systemTask;      private static String every_time_run;      static {          Properties prop = new Properties();          InputStream inStrem = TempFileManager.class.getClassLoader()                  .getResourceAsStream("tempfile.properties");          try {              prop.load(inStrem);              System.out.println(inStrem);            every_time_run = prop.getProperty("every_time_run");          } catch (IOException e) {              e.printStackTrace();          } finally {              try {                  inStrem.close();              } catch (IOException e) {                  e.printStackTrace();              }          }      }      // 监听器初始方法      public void contextInitialized(ServletContextEvent sce) {          timer = new Timer();          systemTask = new SystemTaskTest(sce.getServletContext()                  .getRealPath("/"), sce.getServletContext());          try {              System.out.println("定时器已启动");            // 监听器获取网站的根目录              String path = sce.getServletContext().getRealPath("/");              Long time = Long.parseLong(every_time_run) * 1000;// 循环执行的时间              System.out.println("time" + time);               // 第一个参数是要运行的代码,第二个参数是从什么时候开始运行,第三个参数是每隔多久在运行一次。重复执行              timer.schedule(systemTask, 10000, time);              System.out.println("已经添加任务调度表");        } catch (Exception e) {              e.printStackTrace();        }      }      public void contextDestroyed(ServletContextEvent sce) {          try {              timer.cancel();          } catch (Exception e) {          }      }  }  /**   * 时间任务器   *  */  class SystemTaskTest extends TimerTask {      private ServletContext context;      private String path;      public SystemTaskTest(String path, ServletContext context) {          this.path = path;          this.context = context;      }      /**       * 把要定时执行的任务就在run中       */      public void run() {           TempFileManager etf;             try {              System.out.println("开始执行任务!");            // 需要执行的代码              System.out.println(new Date().toLocaleString());              etf = new TempFileManager(path);              etf.run();                    System.out.println("指定任务执行完成!");        } catch (Exception e) {              e.printStackTrace();          }      }  }  

上面只是监听器,负责定时调用删除临时文件的方法,具体实现是下面的类

package util.upload;import java.io.File;import java.io.IOException;import java.io.InputStream;import java.util.Date;import java.util.Properties;/**   * 删除服务器上的文件   *  */  public class TempFileManager implements Runnable {      private String path;//路径      private static String RETENTION_TIME = "1440";// 文件保存的时间  一天单位分    static {          Properties prop = new Properties();          InputStream inStrem = TempFileManager.class.getClassLoader()                  .getResourceAsStream("execl.properties");          try {              prop.load(inStrem);              RETENTION_TIME = prop.getProperty("file_retention_time");          } catch (IOException e) {              e.printStackTrace();          } finally {              try {                  inStrem.close();              } catch (IOException e) {                  e.printStackTrace();              }          }      }      /**       * 构造函数。初始化参数       * @param path       */      public TempFileManager(String path) {          this.path = path;      }      /**       * 把线程要执行的代码放在run()中       */      public void run() {          System.out.println("文件管理开始=========");          path = path + "upload";          System.out.println("文件管理路径===" + path);          File file = new File(path);          deletefiles(file);      }      /**       * 批量删除文件       *        * @param folder       */      public void deletefiles(File folder) {     if(folder.isDirectory()){        File[] files = folder.listFiles();        if(files.length<=0){            if(!folder.getAbsolutePath().equalsIgnoreCase(path)){            if(canDeleteFile(folder)){                if (folder.delete()) {                      System.out.println("文件夹" + folder.getName() + "删除成功!");                  } else {                      System.out.println("文件夹" + folder.getName()                              + "删除失败!此文件夹内的文件可能正在被使用");                  }              }            }        }        for (int i = 0; i < files.length; i++) {              if(files[i].isDirectory())            {            deletefiles(files[i]);            }else{                deleteFile(files[i]);             }        }      }    }      /**       * 删除文件       *        * @param file       */      private void deleteFile(File file) {          try {              if (file.isFile()) {                  // 删除符合条件的文件                  if (canDeleteFile(file)) {                      if (file.delete()) {                          System.out.println("文件" + file.getName() + "删除成功!");                      } else {                          System.out.println("文件" + file.getName()                                  + "删除失败!此文件可能正在被使用");                      }                  } else {                  }              } else {                  System.out.println("没有可以删除的文件了");              }          } catch (Exception e) {              System.out.println("删除文件失败========");              e.printStackTrace();          }      }      /**       * 判断文件是否能够被删除       */      private boolean canDeleteFile(File file) {          Date fileDate = getfileDate(file);          Date date = new Date();          long time = (date.getTime() - fileDate.getTime()) / 1000 / 60                  - Integer.parseInt(RETENTION_TIME);// 当前时间与文件间隔的分钟  //        System.out.println("time=="+time);        if (time > 0) {              return true;          } else {              return false;          }      }      /**       * 获取文件最后的修改时间       *        * @param file       * @return       */      private Date getfileDate(File file) {          long modifiedTime = file.lastModified();          Date d = new Date(modifiedTime);          return d;      }  }  

判断文件是否超时,超时就自动删除,并且能自动删除文件夹。时时保持上传临时文件夹下干干净净,处女座最爱。。。
还有个配置文件,代码里能看到使用了那些参数,就不贴了,功能很简单,但也做了很久,搜了很多资料,感谢互联网上的各位分享知识的大神,没有他们,就没有这一切。再次感谢各位,虽然忘了看的哪些大神的文章做出来的了。。。如有不对,请指出,谢谢阅读。

0 0