上传文件是指把客户端的文件发送到服务器端,当客户端向服务器上传文件时,客户端发送的HTTP请求正文采用“multipart/form-data”数据类型,它表示复杂的包含多个子部分的复合表单。比如,如下HTML代码中,就包含了普通的文本输入框和两个用于指定上传文件的文件域。
当请求正文为"multipart/form-data"数据类型时,Servlet直接从HttpServletRequest对象中解析复合表单的每个字部份仍然是一项非常复杂的工作。为了简化对"multipart/form-data"类型数据的处理过程,Apache提供了两个软件包来实现文件的上传:
(1)、fileupload软件包(commons-fileupload-版本号.jar):负责上传文件的软件包。
(2)、io软件包(commons-io-版本号.jar):负责输入和输出的软件包。
这两个jar包都可以在Apache的官网中下载,网址分别是http://commons.apache.org/fileupload/ 和 http://commons.apache.org/io/ ,Servlet主要利用fileupload软件包的接口和类来实现文件的上传,而fileuoload软件本身依赖io软件包,所以两者缺一不可。
对于一个正文部分为“multipart/form-data”类型的Http请求,fileupload软件包把请求正文包含的复合表单中的每个子部分看作是一个FileItem对象,FileItem对象分为两类。
(1)、formFieId:普通表单域类型,表单中的文本框、单选、复选及提交按钮等常见表单元素
(2)、非formFieId:上传文件类型,表单中的文件域就是这种类型。
FileItemFactory是创建FileItem对象的工厂,DiskFileItemFactory类和DiskFileItem类分别实现了FileItemFactory接口和FileItem接口。DiskFileItemFactory是创建DiskFileItem对象的工厂,而DiskFileItem类表示基于硬盘的FileItem,它能够把客户端上传的文件数据保存到硬盘上。
(1)、以下程序代码创建了一个DiskFileItemFactory对象,然后设置了在向硬盘写数据时所用的缓冲区大小,以及所使用的零食目录。(fileuoload软件包自身实现中,为了提高向硬盘写数据的效率,尤其是写大容量数据的效率,fileuoload软件包在写数据时会使用缓存,以及向临时目录存放一些临时数据,当然这不一定需要开发人员去设置)
//常见一个基于硬盘的FileItem工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//设置向硬盘写数据时所用的缓冲区大小,此处为4M
factory.setSizeThreshold(4*1024*1024);
//设置临时目录
factory.setRepository(new File(tempFilePath));
(2)、ServletFileUpload类为文件的上传处理器,它与FileItemFactory关联。以下程序代码创建了一个ServletFileUpload对象,它与一个DiskFileItemFactory对象关联。该类存在setSizeMax()方法用来设置上传文件的最大尺寸。
//创建一个文件上传处理器
ServletFileUpload upload = new ServletFileUpload(factory);
//设置上传文件的最大尺寸,此处为4M
upload.setSizeMax(4*1024*1024);
(3)、创建好了文件处理器之后,ServletFileUpload提供了parseRequest(HttpServletRequest request)方法来解析HttpServletRequest对象中的复合表单数据,它返回包含一组FileItem对象的List集合。代码如下:
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//常见一个基于硬盘的FileItem工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//创建一个文件上传处理器
ServletFileUpload upload = new ServletFileUpload(factory);
//解析复合表单数据,将结果存储到List对象中
List items = upload.parseRequest(request);
}
(4)、在得到了包含FileItem对象的List集合后,就可以遍历这个集合,判断每个FileItem对象的类型,然后做出相应的处理。
Iterator iter = items.iterator();
while(iter.hasNext()){
FileItem item = (FileItem)iter.next();
//判断FileItem类型
if(item.isFormFileId()){ //处理普通的表单域
//获取参数名
String name = item.getFieIdName();
//获取参数值
String value = item.getString();
}else{ //处理文件类型
//注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的
String filename = item.getName();
//处理获取到的上传文件的文件名的路径部分,只保留文件名部分,所以从最后一个\符号的下一个字符开始截取字符串。
filename = filename.substring(filename.lastIndexOf("\\")+1);
//获取item中的上传文件的输入流
InputStream in = item.getInputStream();
//创建一个文件输出流
FileOutputStream out = new FileOutputStream(savePath + "\\" + filename);
//创建一个缓冲区
byte buffer[] = new byte[1024];
//判断输入流中的数据是否已经读完的标识
int len = 0;
//循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据
while((len=in.read(buffer))>0){
//使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "\\" + filename)当中
out.write(buffer, 0, len);
}
//关闭输入流
in.close();
//关闭输出流
out.close();
//删除处理文件上传时生成的临时文件
item.delete();
}
}
做完上述的所有代码之后,运行服务器访问前段页面,输入内容、上传指定文件
控制台输出语句:
指定目录的文件内容: