在Servlet中利用Apache开源类库实现文件上传
本文结合具体的范例,介绍如何通过Apache开源类库提供的软件包,在Servlet中进行文件上传。本文的参考书籍是《Tomcat与Java Web开发技术详解》第三版,作者:孙卫琴。
本文所用的软件版本为:Window10,JDK10,Tomcat9。
本文所涉及的源代码的下载网址为:
http://www.javathinker.net/javaweb/upload-app.rar
Apache开源软件组织提供了与文件上传有关的两个软件包:
fileupload软件包(commons-fileupload-X.jar):负责上传文件的软件包,下载网址为:http://commons.apache.org/fileupload/。
该软件包的使用说明文档的网址为:http://commons.apache.org/fileupload/using.html。
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软件包中的主要接口和类的类框图。
图1-1 fileupload软件包中的主要接口和类的类框图
如下图1-2所示,对于一个正文部分为“multipart/form-data”类型的HTTP请求,uploadfile软件包把请求正文包含的复合表单中的每个子部分看作是一个FileItem对象。FileItem对象分为两种类型:
formField:普通表单域类型,表单中的文本域以及提交按钮等都是这种类型。
非formField:上传文件类型,表单中的文件域就是这种类型,它包含了文件数据。
图1-2 复合表单中的每个子部分看作是一个FileItem对象
FileItemFactory是创建FileItem对象的工厂。DiskFileItemFactory类和DiskFileItem类分别实现了FileItemFactory接口和FileItem接口。DiskFileItem类表示基于硬盘的FileItem,DiskFileItem类能够把客户端上传的文件数据保存到硬盘上。DiskFileItemFactory则是创建DiskFileItem对象的工厂。
以下程序代码创建了一个DiskFileItemFactory对象,然后设置向硬盘写数据时所用的缓冲区的大小,以及所使用的临时目录。在fileupload软件包自身的实现中,为了提高向硬盘写数据的效率,尤其是写大容量数据的效率,fileupload软件包在写数据时会使用缓存,以及向临时目录存放一些临时数据。
//创建一个基于硬盘的FileItem工厂DiskFileItemFactory factory = new DiskFileItemFactory();//设置向硬盘写数据时所用的缓冲区的大小,此处为4Kfactory.setSizeThreshold(4*1024); //设置临时目录factory.setRepository(new File(tempFilePath));
ServletFileUpload类为文件上传处理器,它与FileItemFactory关联。以下程序代码创建了一个ServletFileUpload对象,它与一个DiskFileItemFactory对象关联。ServletFileUpload类的setSizeMax()方法用来设置允许上传的文件的最大尺寸。
//创建一个文件上传处理器ServletFileUpload upload = new ServletFileUpload(factory);//设置允许上传的文件的最大尺寸,此处为4Mupload.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页面。
图1-3 upload.htm页面
对于图1-3的upload.htm页面,在表单中提供图中所示的数据,然后提交表单,UploadServlet就会响应本次请求,把“FromClient.rar”以及“FromClient.txt”文件都保存到helloapp/store目录下,并且向客户端返回如图1-4所示的HTML页面。
图1-4 UploadServlet返回的HTML页面
UploadServlet调用ServletFileUpload对象的setSizeMax(410241024)方法,把允许上传的文件的最大尺寸设为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)
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。