Servlet 3.0笔记之超方便的文件上传支持

在以前,处理文件上传是一个很痛苦的事情,大都借助于开源的上传组件,诸如commons fileupload等。现在好了,很方便,便捷到比那些组件都方便至极。以前的HTML端上传表单不用改变什么,还是一样的 multipart/form-data MIME类型。
让Servlet支持上传,需要做两件事情
  1. 需要添加MultipartConfig注解
  2. 从request对象中获取Part文件对象
但在具体实践中,还是有一些细节处理,诸如设置上传文件的最大值,上传文件的保存路径。
需要熟悉MultipartConfig注解,标注在@WebServlet之上,具有以下属性:
属性名 类型 是否可选 描述
fileSizeThreshold int 当数据量大于该值时,内容将被写入文件。
location String 存放生成的文件地址。
maxFileSize long 允许上传的文件最大值。默认值为 -1,表示没有限制。
maxRequestSize long 针对该 multipart/form-data 请求的最大数量,默认值为 -1,表示没有限制。

一些实践建议:
  1. 若是上传一个文件,仅仅需要设置maxFileSize熟悉即可。
  2. 上传多个文件,可能需要设置maxRequestSize属性,设定一次上传数据的最大量。
  3. 上传过程中无论是单个文件超过maxFileSize值,或者上传总的数据量大于maxRequestSize值都会抛出IllegalStateException异常;
  4. location属性,既是保存路径(在写入的时候,可以忽略路径设定),又是上传过程中临时文件的保存路径,一旦执行Part.write方法之后,临时文件将被自动清除。
  5. 但Servlet 3.0规范同时也说明,不提供获取上传文件名的方法,尽管我们可以通过part.getHeader("content-disposition")方法间接获取得到。
  6. 如何读取MultipartConfig注解属性值,API没有提供直接读取的方法,只能手动获取。
    来一个示例吧,上传前台页面:
    后台处理Servlet:
    /**
     * 上传文件测试 location为临时文件保存路径
     * 
     * @author yongboy
     * @date 2011-1-13
     * @version 1.0
     */
    @MultipartConfig(location = "/home/yongboy/tmp/", maxFileSize = 1024 * 1024 * 10)
    @WebServlet("/upload")
    public class UploadFileAction extends HttpServlet {
     private static final long serialVersionUID = 92166165626L;
     private static final Log log = LogFactory.getLog(UploadFileAction.class);
     // 得到注解信息
     private static final MultipartConfig config;
    
     static {
      config = UploadFileAction.class.getAnnotation(MultipartConfig.class);
     }
    
     protected void doGet(HttpServletRequest request,
       HttpServletResponse response) throws ServletException, IOException {
      request.getRequestDispatcher("/upload.jsp").forward(request, response);
     }
    
     protected void doPost(HttpServletRequest request,
       HttpServletResponse response) throws ServletException, IOException {
      // 为避免获取文件名称时出现乱码
      request.setCharacterEncoding("UTF-8");
    
      Part part = null;
      try {
       // <input name="file" size="50" type="file" />
       part = request.getPart("file");
      } catch (IllegalStateException ise) {
       // 上传文件超过注解所标注的maxRequestSize或maxFileSize值
       if (config.maxRequestSize() == -1L) {
        log.info("the Part in the request is larger than maxFileSize");
       } else if (config.maxFileSize() == -1L) {
        log.info("the request body is larger than maxRequestSize");
       } else {
        log.info("the request body is larger than maxRequestSize, or any Part in the request is larger than maxFileSize");
       }
    
       forwardErrorPage(request, response, "上传文件过大,请检查输入是否有误!");
       return;
      } catch (IOException ieo) {
       // 在接收数据时出现问题
       log.error("I/O error occurred during the retrieval of the requested Part");
      } catch (Exception e) {
       log.error(e.toString());
       e.printStackTrace();
      }
    
      if (part == null) {
       forwardErrorPage(request, response, "上传文件出现异常,请检查输入是否有误!");
       return;
      }
      
      // 得到文件的原始名称,eg :测试文档.pdf
      String fileName = UploadUtils.getFileName(part);
    
      log.info("contentType : " + part.getContentType());
      log.info("fileName : " + fileName);
      log.info("fileSize : " + part.getSize());
      log.info("header names : ");
      for (String headerName : part.getHeaderNames()) {
       log.info(headerName + " : " + part.getHeader(headerName));
      }
    
      String saveName = System.currentTimeMillis() + "."
        + FilenameUtils.getExtension(fileName);
    
      log.info("save the file with new name : " + saveName);
    
      // 因在注解中指定了路径,这里可以指定要写入的文件名
      // 在未执行write方法之前,将会在注解指定location路径下生成一临时文件
      part.write(saveName);
    
      request.setAttribute("fileName", fileName);
      request.getRequestDispatcher("/uploadResult.jsp").forward(request,
        response);
     }
    
     private void forwardErrorPage(HttpServletRequest request,
       HttpServletResponse response, String errMsg)
       throws ServletException, IOException {
      request.setAttribute("errMsg", errMsg);
    
      request.getRequestDispatcher("/upload.jsp").forward(request, response);
     }
    }
    
    获取文件名的函数,很简单:
     /**
      * 如何得到上传的文件名, API没有提供直接的方法,只能从content-disposition属性中获取
      * 
      * @param part
      * @return
      */
     protected static String getFileName(Part part) {
      if (part == null)
       return null;
    
      String fileName = part.getHeader("content-disposition");
      if (StringUtils.isBlank(fileName)) {
       return null;
      }
    
      return StringUtils.substringBetween(fileName, "filename=\"", "\"");
     }
     
     
    文件上传成功之后,以及日志输出的截图如下:
    截图中可以看到Part包含 content-disposition属性,可以很容易从值中抽取出文件名。临时生成的上传文件大都以 .tmp为后缀,大致如下:
    让上传出现错误,就可以在保存路径下看到大致如上的临时文件。
    一次上传多个文件的后台servlet示范:
    /**
     * 多文件上传支持
     * @author yongboy
     * @date 2011-1-14
     * @version 1.0
     */
    @MultipartConfig(
      location = "/home/yongboy/tmp/", 
      maxFileSize = 1024L * 1024L, // 每一个文件的最大值
      maxRequestSize = 1024L * 1024L * 10L // 一次上传最大值,若每次只能上传一个文件,则设置maxRequestSize意义不大
    )
    @WebServlet("/uploadFiles")
    public class UploadFilesAction extends HttpServlet {
     private static final long serialVersionUID = 2304820820384L;
     private static final Log log = LogFactory.getLog(UploadFilesAction.class);
    
     protected void doGet(HttpServletRequest request,
       HttpServletResponse response) throws ServletException, IOException {
      request.getRequestDispatcher("/uploads.jsp").forward(request, response);
     }
    
     protected void doPost(HttpServletRequest request,
       HttpServletResponse response) throws ServletException, IOException {
      request.setCharacterEncoding("UTF-8");
    
      Collection parts = null;
      try {
       parts = request.getParts();
      } catch (IllegalStateException ise) {
       // 可能某个文件大于指定文件容量maxFileSize,或者提交数据大于maxRequestSize
       log.info("maybe the request body is larger than maxRequestSize, or any Part in the request is larger than maxFileSize");
      } catch (IOException ioe) {
       // 在获取某个文件时遇到拉IO异常错误
       log.error("an I/O error occurred during the retrieval of the Part components of this request");
      } catch (Exception e) {
       log.error("the request body is larger than maxRequestSize, or any Part in the request is larger than maxFileSize");
       e.printStackTrace();
      }
    
      if (parts == null || parts.isEmpty()) {
       doError(request, response, "上传文件为空!");
       return;
      }
    
      // 前端具有几个file组件,这里会对应几个Part对象
      List fileNames = new ArrayList();
      for (Part part : parts) {
       if (part == null) {
        continue;
       }
       // 这里直接以源文件名保存
       String fileName = UploadUtils.getFileName(part);
    
       if (StringUtils.isBlank(fileName)) {
        continue;
       }
    
       part.write(fileName);
       fileNames.add(fileName);
      }
    
      request.setAttribute("fileNames", fileNames);
      request.getRequestDispatcher("/uploadsResult.jsp").forward(request,
        response);
     }
    
     private void doError(HttpServletRequest request,
       HttpServletResponse response, String errMsg)
       throws ServletException, IOException {
      request.setAttribute("errMsg", errMsg);
    
      this.doGet(request, response);
     }
    }
    • 0
      点赞
    • 0
      收藏
      觉得还不错? 一键收藏
    • 0
      评论

    “相关推荐”对你有帮助么?

    • 非常没帮助
    • 没帮助
    • 一般
    • 有帮助
    • 非常有帮助
    提交
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值