>>分享孙卫琴的Java技术专稿和著作 书籍支持  卫琴直播  品书摘要  在线测试  资源下载  联系我们
发表一个新主题 开启一个新投票 回复文章 您是本文章第 24665 个阅读者 刷新本主题
 * 贴子主题:  【JavaWeb专题】在Servlet中利用Apache开源类库实现文件上传 回复文章 点赞(0)  收藏  
作者:sunweiqin    发表时间:2021-09-11 05:58:12     消息  查看  搜索  好友  邮件  复制  引用

本文参考《Tomcat与Java Web开发技术详解》,作者:孙卫琴,电子工业出版社出版

本文结合具体的范例,介绍如何通过Apache开源类库提供的软件包,在Servlet中进行文件上传。
本文所用的软件版本为:Window10,JDK10,Tomcat9。
本文所涉及的源代码的下载网址为:
http://www.javathinker.net/javaweb/upload-app.rar

Apache开源软件组织提供了与文件上传有关的两个软件包:
(1) fileupload软件包(commons-fileupload-X.jar):负责上传文件的软件包,下载网址为:http://commons.apache.org/fileupload/。
该软件包的使用说明文档的网址为:http://commons.apache.org/fileupload/using.html。
(2) I/O软件包(commons-io-X.jar):负责输入输出的软件包,下载网址为:http://commons.apache.org/io/。

应该把这两个软件包的JAR文件放在helloapp/WEB-INF/lib目录下。本书配套源代码包的sourcecode/chapter05/helloapp/WEB-INF/lib目录下已经提供了以上两个JAR文件。Servlet主要利用fileupload软件包中的接口和类来实现文件上传,而fileupload软件包本身依赖I/O软件包。以下图1-1为fileupload软件包中的主要接口和类的类框图。
点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小  
图1-1  fileupload软件包中的主要接口和类的类框图

如下图1-2所示,对于一个正文部分为“multipart/form-data”类型的HTTP请求,uploadfile软件包把请求正文包含的复合表单中的每个子部分看作是一个FileItem对象。FileItem对象分为两种类型:
(1) formField:普通表单域类型,表单中的文本域以及提交按钮等都是这种类型。
(2) 非formField:上传文件类型,表单中的文件域就是这种类型,它包含了文件数据。

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小  
图1-2  复合表单中的每个子部分看作是一个FileItem对象

FileItemFactory是创建FileItem对象的工厂。DiskFileItemFactory类和DiskFileItem类分别实现了FileItemFactory接口和FileItem接口。DiskFileItem类表示基于硬盘的FileItem,DiskFileItem类能够把客户端上传的文件数据保存到硬盘上。DiskFileItemFactory则是创建DiskFileItem对象的工厂。

以下程序代码创建了一个DiskFileItemFactory对象,然后设置向硬盘写数据时所用的缓冲区的大小,以及所使用的临时目录。在fileupload软件包自身的实现中,为了提高向硬盘写数据的效率,尤其是写大容量数据的效率,fileupload软件包在写数据时会使用缓存,以及向临时目录存放一些临时数据。
//创建一个基于硬盘的FileItem工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//设置向硬盘写数据时所用的缓冲区的大小,此处为4K
factory.setSizeThreshold(4*1024);
//设置临时目录
factory.setRepository(new File(tempFilePath));

ServletFileUpload类为文件上传处理器,它与FileItemFactory关联。以下程序代码创建了一个ServletFileUpload对象,它与一个DiskFileItemFactory对象关联。ServletFileUpload类的setSizeMax()方法用来设置允许上传的文件的最大尺寸。

//创建一个文件上传处理器
ServletFileUpload upload = new ServletFileUpload(factory);
//设置允许上传的文件的最大尺寸,此处为4M
upload.setSizeMax(4*1024*1024);
ServletFileUpload类的parseRequest(HttpServletRequest req)方法能够解析HttpServletRequest对象中的复合表单数据,返回包含一组FileItem对象的List集合:
List<FileItem> items = upload.parseRequest(request);    

得到了包含FileItem对象的List集合后,就可以遍历这个集合,判断每个FileItem对象的类型,然后做出相应的处理。

for(FileItem item:items){  //遍历集合中的每个FileItem对象
  if(item.isFormField()) {
    processFormField(item,out); //处理普通的表单域
  }else{
    processUploadedFile(item,out); //处理上传文件
  }
}

以下例程1-1的UploadServlet类利用fileupload软件包来处理用户在upload.htm页面中上传的文件。

例程1-1  UploadServlet.java

package mypack;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import org.apache.commons.fileupload.*;
import org.apache.commons.fileupload.servlet.*;
import org.apache.commons.fileupload.disk.*;

public class UploadServlet extends HttpServlet {
  private String filePath; //存放上传文件的目录
  private String tempFilePath; //存放临时文件的目录

  public void init(ServletConfig config)throws ServletException {
    super.init(config);
    filePath=config.getInitParameter("filePath");
    tempFilePath=config.getInitParameter("tempFilePath");
    filePath=getServletContext().getRealPath(filePath);
    tempFilePath=getServletContext().getRealPath(tempFilePath);
  }
  public void doPost(HttpServletRequest request,
         HttpServletResponse response)
         throws ServletException, IOException {
    response.setContentType("text/plain");
    //向客户端发送响应正文
    PrintWriter out=response.getWriter();
    try{
      //创建一个基于硬盘的FileItem工厂
      DiskFileItemFactory factory = new DiskFileItemFactory();
      //设置向硬盘写数据时所用的缓冲区的大小,此处为4K
      factory.setSizeThreshold(4*1024);
      //设置临时目录
      factory.setRepository(new File(tempFilePath));

      //创建一个文件上传处理器
      ServletFileUpload upload = new ServletFileUpload(factory);
      //设置允许上传的文件的最大尺寸,此处为4M
      upload.setSizeMax(4*1024*1024);
    
      List<FileItem> items = upload.parseRequest(request);    

      for(FileItem item:items){
        if(item.isFormField()) {
          processFormField(item,out); //处理普通的表单域
        }else{
          processUploadedFile(item,out); //处理上传文件
        }
      }
      out.close();
    }catch(Exception e){
       throw new ServletException(e);
    }
  }

  private void processFormField(FileItem item,PrintWriter out){
    String name = item.getFieldName();
    String value = item.getString();
    out.println(name+":"+value+"\r\n");
  }
    
  private void processUploadedFile(FileItem item,
               PrintWriter out)throws Exception{
    String filename=item.getName();
    int index=filename.lastIndexOf("\\");
    filename=filename.substring(index+1,filename.length());
    long fileSize=item.getSize();
    
    if(filename.equals("") && fileSize==0)return;

    File uploadedFile = new File(filePath+"/"+filename);
    item.write(uploadedFile);
    out.println(filename+" is saved.");
    out.println("The size of " +filename+" is "+fileSize+"\r\n");
  }

web.xml文件中UploadServlet的配置代码如下:

<servlet>
  <servlet-name>upload</servlet-name>
  <servlet-class>mypack.UploadServlet</servlet-class>
  <init-param>
    <param-name>filePath</param-name>
    <param-value>store</param-value>
  </init-param>
  <init-param>
    <param-name>tempFilePath</param-name>
    <param-value>temp</param-value>
  </init-param>
</servlet>

<servlet-mapping>
  <servlet-name>upload</servlet-name>
  <url-pattern>/upload</url-pattern>
</servlet-mapping>

UploadServlet有两个初始化参数:filePath参数表示UploadServlet用于存放上传文件的目录;tempFilePath参数表示fileupload软件包用于存放临时文件的目录。
以下例程1-2的upload.htm定义了一个用于上传文件的复合表单,它有一个名字为“username”的文本域,还有两个用于指定上传文件的文件域。

例程1-2  upload.htm

<html>
<head>
<title>Upload</title>
</head>
<body >
  <form name="uploadForm" method="POST"
    enctype="MULTIPART/FORM-DATA"
    action="upload">
    <table>
      <tr>
       <td><div align="right">User Name:</div></td>
       <td><input type="text" name="username" size="30"/> </td>
      </tr>
      <tr>
       <td><div align="right">Upload File1:</div></td>
       <td><input type="file" name="file1" size="30"/> </td>
      </tr>
      <tr>
        <td><div align="right">Upload File2:</div></td>
        <td><input type="file" name="file2" size="30"/> </td>
      </tr>
      <tr>
        <td><input type="submit" name="submit" value="upload"></td>
        <td><input type="reset" name="reset" value="reset"></td>
      </tr>
    </table>
  </form>
</body>
</html>

从浏览器中访问http://localhost:8080/helloapp/upload.htm,将出现如图1-3所示的HTML页面。
点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小  
图1-3  upload.htm页面

对于图1-3的upload.htm页面,在表单中提供图中所示的数据,然后提交表单,UploadServlet就会响应本次请求,把“FromClient.rar”以及“FromClient.txt”文件都保存到helloapp/store目录下,并且向客户端返回如图1-4所示的HTML页面。
点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小  
图1-4  UploadServlet返回的HTML页面

UploadServlet调用ServletFileUpload对象的setSizeMax(4*1024*1024)方法,把允许上传的文件的最大尺寸设为4M。如果在图1-3的upload.htm页面中输入一个大小超过4M的文件,那么UploadServlet在处理上传文件时会抛出以下异常:

org.apache.commons.fileupload.FileUploadBase
$SizeLimitExceededException:
the request was rejected because its size (7859197)
exceeds the configured maximum (4194304)

程序猿的技术大观园:www.javathinker.net



[这个贴子最后由 admin 在 2021-10-09 10:40:10 重新编辑]
  Java面向对象编程-->Java语言中的修饰符
  JavaWeb开发-->Servlet技术详解(Ⅲ)
  JSP与Hibernate开发-->Java应用分层架构及软件模型
  Java网络编程-->基于UDP的数据报和套接字
  精通Spring-->绑定表单
  Vue3开发-->Vue简介
  【Vue.js技术专题】Vue组件的命名规则
  【Vue.js技术专题】Vue组件的单向数据流
  【Vue.js技术专题】命名路由
  【Vue.js技术专题】Vuex中异步操作
  【Java基础编程专题】浮点数的格式化以及运算精度
  视频:我的Java课堂总动员,带领小白们走马观花看Java江湖
  【持久化专题】映射一对多双向关联关系
  【持久化专题】用@MapsId注解映射派生主键
  【持久化专题】用@Formula注解映射派生属性
  【持久化专题】从JPA API中获得Hibernate API
  【持久化专题】@Access注解设定Hibernate访问类的属性的方式
  【持久化专题】JPA的事件处理API的用法
  【Java网络编程专题】用Java套接字访问HTTP服务器读取网页数...
  【Java网络编程专题】优化访问数据库的程序代码的一些技巧
  【Java基础编程专题】Java继承的利弊和使用原则
  更多...
 IPIP: 已设置保密
楼主      
1页 0条记录 当前第1
发表一个新主题 开启一个新投票 回复文章


中文版权所有: JavaThinker技术网站 Copyright 2016-2026 沪ICP备16029593号-2
荟萃Java程序员智慧的结晶,分享交流Java前沿技术。  联系我们
如有技术文章涉及侵权,请与本站管理员联系。