热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

java基于servlet实现文件上传功能解析

这篇文章主要为大家详细介绍了java基于servlet实现上传功能,后台使用java实现,前端主要是js的ajax实现,感兴趣的小伙伴们可以参考一下

最近项目需要做一个文件上传功能,做完了分享下,顺带当做笔记。
上传功能用后台用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 items=upload.parseRequest(request);  
  Iterator 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 map=new HashMap();
      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


  
  
    
    
    
  
  
    添加
    
0

界面比较简单,就一个添加的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文件

//显示上传信息的html
var upfile_html = '
' + '0%
'; var targetDIV_id = "target";//显示上传文件的目标div的ID var 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";//上传文件的请求url var gurl = "getProgress";//获取上传进度信息的url var cancelFlag = 0;//取消标志 var timer, waittimer;//定时器 var nowID = 0;//正在上传文件的id var 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 0) { var uf; for ( var i = 0; i 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

使用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  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; 
  } 

} 

判断文件是否超时,超时就自动删除,并且能自动删除文件夹。

以上就是本文的全部内容,希望对大家学习java程序设计有所帮助。


推荐阅读
  • Allegro总结:1.防焊层(SolderMask):又称绿油层,PCB非布线层,用于制成丝网印板,将不需要焊接的地方涂上防焊剂.在防焊层上预留的焊盘大小要比实际的焊盘大一些,其差值一般 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • 在springmvc框架中,前台ajax调用方法,对图片批量下载,如何弹出提示保存位置选框?Controller方法 ... [详细]
  • 本文介绍了前端人员必须知道的三个问题,即前端都做哪些事、前端都需要哪些技术,以及前端的发展阶段。初级阶段包括HTML、CSS、JavaScript和jQuery的基础知识。进阶阶段涵盖了面向对象编程、响应式设计、Ajax、HTML5等新兴技术。高级阶段包括架构基础、模块化开发、预编译和前沿规范等内容。此外,还介绍了一些后端服务,如Node.js。 ... [详细]
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
  • 本文介绍了如何使用JSONObiect和Gson相关方法实现json数据与kotlin对象的相互转换。首先解释了JSON的概念和数据格式,然后详细介绍了相关API,包括JSONObject和Gson的使用方法。接着讲解了如何将json格式的字符串转换为kotlin对象或List,以及如何将kotlin对象转换为json字符串。最后提到了使用Map封装json对象的特殊情况。文章还对JSON和XML进行了比较,指出了JSON的优势和缺点。 ... [详细]
  • 本文介绍了如何使用jQuery和AJAX来实现动态更新两个div的方法。通过调用PHP文件并返回JSON字符串,可以将不同的文本分别插入到两个div中,从而实现页面的动态更新。 ... [详细]
  • BZOJ1233 干草堆单调队列优化DP
    本文介绍了一个关于干草堆摆放的问题,通过使用单调队列来优化DP算法,求解最多可以叠几层干草堆。具体的解题思路和转移方程在文章中进行了详细说明,并给出了相应的代码示例。 ... [详细]
  • 关于extjs开发实战pdf的信息
    本文目录一览:1、extjs实用开发指南2、本 ... [详细]
  • asp.net(vb脚本)如何获取xml的节点值?xmlversion1.0encodingutf-8?rootimageimagemenusmenuurl#frame_paren ... [详细]
  • 1、概述首先和大家一起回顾一下Java消息服务,在我之前的博客《Java消息队列-JMS概述》中,我为大家分析了:然后在另一篇博客《Java消息队列-ActiveMq实战》中 ... [详细]
  • 前言对于从事技术的人员来说ajax是这好东西,都会使用,而且乐于使用。但对于新手,开发一个ajax实例,还有是难度的,必竟对于他们这是新东西。leo开发一个简单的ajax实例,用的是 ... [详细]
author-avatar
mobiledu2502912637
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有