深入体验JavaWeb开发内幕—高级特性张孝祥著站点应用中都需要为用户提供通过浏览器上传文档资料的功能,例如,上传邮件附件、个人相片、共享资料等。对文件上传功能,在浏览器端提供了较好的支持,只要将FORM表单的enctype属性设置为“multipart/form-data”即可;但在Web服务器端如何获取浏览器上传的文件,需要进行复杂的编程处理。为了简化和帮助Web开发人员接收浏览器上传的文件,一些公司和组织专门开发了文件上传组件。本章将详细介绍如何使用Apache文件上传组件,以及分析该组件源程序的设计思路和实现方法。1.1准备实验环境按下面的步骤为本章的例子程序建立运行环境:(1)在Tomcat5.5.12的tomcat的安装目录\webapps目录中创建一个名为fileupload的子目录,并在fileupload目录中创建一个名为test.html的网页文件,在该文件中写上“这是test.html页面的原始内容!”这几个字符。(2)在tomcat的安装目录\webapps\fileupload目录中创建一个名为WEB-INF的子目录,在WEB-INF目录中创建一个名为classes的子目录和一个web.xml文件,web.xml文件内容如下:web-app/web-app(3)要使用Apache文件上传组件,首先需要安装Apache文件上传组件包。在tomcat的安装目录\webapps\fileupload\WEB-INF目录中创建一个名为lib的子目录,然后从网址下载到Apache组件的二进制发行包,在本书的附带带光盘中也提供了该组件的二进制发行包,文件名为commons-fileupload-1.0.zip。从commons-fileupload-1.0.zip压缩包中解压出commons-fileupload-1.0.jar文件,将它放置进tomcat的安装目录\webapps\fileupload\WEB-INF\lib目录中,就完成了Apache文件上传组件的安装。(4)在tomcat的安装目录\webapps\fileupload目录中创建一个名为src的子目录,src目录用于放置本章编写的Java源程序。为了便于对Servlet源文件进行编译,在src目录中编写一个compile.bat批处理文件,如例程1-1所示。例程1-1compile.batsetPATH=C:\jdk1.5.0_01\bin;%path%setCLASSPATH=C:\tomcat-5.5.12\common\lib\servlet-api.jar;C:\tomcat-5.5.12\\webapps\fileupload\WEB-INF\lib\commons-fileupload-1.0.jar;%CLASSPATH%javac-d..\WEB-INF\classes%1pause在compile.bat批处理文件中要注意将commons-fileupload-1.0.jar文件的路径加入到CLASSPATH环境变量中和确保编译后生成的class文件存放到tomcat安装目录\webapps\fileupload\WEB-INF\classes目录中,上面的CLASSPATH环境变量的设置值由于排深入体验JavaWeb开发内幕—高级特性张孝祥著版原因进行了换行,实际上不应该有换行。接着在src目录中为compile.bat文件创建一个快捷方式,以后只要在Windows资源管理器窗口中将Java源文件拖动到compile.bat文件的快捷方式上,就可以完成Java源程序的编译了。之所以要创建compile.bat文件的快捷方式,是因为直接将Java源程序拖动到compile.bat批处理文件时,compile.bat批处理文件内编写的相对路径不被支持。创建完的fileupload目录中的文件结构如图1.1所示。图1.1(4)启动Tomcat,在本地计算机的浏览器地址栏中输入如下地址:验证浏览器能够成功到该网页文档。如果浏览器无法访问到该网页文档,请检查前面的操作步骤和改正问题,直到浏览器能够成功到该网页文档为止。(5)为了让/fileupload这个WEB应用程序能自动重新装载发生了修改的Servlet程序,需要修改Tomcat的server.xml文件,在该文件的Host元素中增加如下一个Context子元素:Contextpath=/fileuploaddocBase=fileuploadreloadable=true/保存server.xml文件后,重新启动Tomcat。1.2Apache文件上传组件的应用JavaWeb开发人员可以使用Apache文件上传组件来接收浏览器上传的文件,该组件由多个类共同组成,但是,对于使用该组件来编写文件上传功能的JavaWeb开发人员来说,只需要了解和使用其中的三个类:DiskFileUpload、FileItem和FileUploadException。这三个类全部位于org.apache.commons.fileupload包中。1.2.1查看API文档在准备实验环境时获得的commons-fileupload-1.0.zip文件的解压缩目录中可以看到一个docs的子目录,其中包含了Apache文件上传组件中的各个API类的帮助文档,从这个文档中可以了解到各个API类的使用帮助信息。打开文件上传组件API帮助文档中的index.html页面,在左侧分栏窗口页面中列出了文件上传组件中的各个API类的名称,在右侧分栏窗口页面的底部列出了一段示例代码,如图1.2所示。深入体验JavaWeb开发内幕—高级特性张孝祥著类的帮助文档,而应该以图1.2中的示例代码为线索,以其中所使用到的类为入口点,按图索骥地进行阅读,对于示例代码中调用到的各个API类的方法则应重点掌握。1.2.2DiskFileUpload类DiskFileUpload类是Apache文件上传组件的核心类,应用程序开发人员通过这个类来与Apache文件上传组件进行交互。下面介绍DiskFileUpload类中的几个常用的重要方法。1.setSizeMax方法setSizeMax方法用于设置请求消息实体内容的最大允许大小,以防止客户端故意通过上传特大的文件来塞满服务器端的存储空间,单位为字节。其完整语法定义如下:publicvoidsetSizeMax(longsizeMax)如果请求消息中的实体内容的大小超过了setSizeMax方法的设置值,该方法将会抛出FileUploadException异常。2.setSizeThreshold方法Apache文件上传组件在解析和处理上传数据中的每个字段内容时,需要临时保存解析出的数据。因为Java虚拟机默认可以使用的内存空间是有限的(笔者测试不大于100M),超出限制时将会发生“java.lang.OutOfMemoryError”错误,如果上传的文件很大,例如上传800M的文件,在内存中将无法保存该文件内容,Apache文件上传组件将用临时文件来保存这些数据;但如果上传深入体验JavaWeb开发内幕—高级特性张孝祥著的文件很小,例如上传600个字节的文件,显然将其直接保存在内存中更加有效。setSizeThreshold方法用于设置是否使用临时文件保存解析出的数据的那个临界值,该方法传入的参数的单位是字节。其完整语法定义如下:publicvoidsetSizeThreshold(intsizeThreshold)3.setRepositoryPath方法setRepositoryPath方法用于设置setSizeThreshold方法中提到的临时文件的存放目录,这里要求使用绝对路径。其完整语法定义如下:publicvoidsetRepositoryPath(StringrepositoryPath)如果不设置存放路径,那么临时文件将被储存在java.io.tmpdir这个JVM环境属性所指定的目录中,tomcat5.5.9将这个属性设置为了“tomcat安装目录/temp/”目录。4.parseRequest方法parseRequest方法是DiskFileUpload类的重要方法,它是对HTTP请求消息进行解析的入口方法,如果请求消息中的实体内容的类型不是“multipart/form-data”,该方法将抛出FileUploadException异常。parseRequest方法解析出FORM表单中的每个字段的数据,并将它们分别包装成独立的FileItem对象,然后将这些FileItem对象加入进一个List类型的集合对象中返回。parseRequest方法的完整语法定义如下:publicListparseRequest(HttpServletRequestreq)parseRequest方法还有一个重载方法,该方法集中处理上述所有方法的功能,其完整语法定义如下:parseRequest(HttpServletRequestreq,intsizeThreshold,longsizeMax,Stringpath)这两个parseRequest方法都会抛出FileUploadException异常。5.isMultipartContent方法isMultipartContent方法方法用于判断请求消息中的内容是否是“multipart/form-data”类型,是则返回true,否则返回false。isMultipartContent方法是一个静态方法,不用创建DiskFileUpload类的实例对象即可被调用,其完整语法定义如下:publicstaticfinalbooleanisMultipartContent(HttpServletRequestreq)6.setHeaderEncoding方法由于浏览器在提交FORM表单时,会将普通表单中填写的文本内容传递给服务器,对于文件上传字段,除了传递原始的文件内容外,还要传递其文件路径名等信息,如后面的图1.3所示。不管FORM表单采用的是“application/x-”编码,还是“multipart/form-data”编码,它们仅仅是将各个FORM表单字段元素内容组织到一起的一种格式,而这些内容又是由某种字符集编码来表示的。关于浏览器采用何种字符集来编码FORM表单字段中的内容,请参看笔者编著的《深入体验javaWeb开发内幕——核心基础》一书中的第6.9.2的讲解,“multipart/form-data”类型的表单为表单字段内容选择字符集编码的原理和方式与“application/x-”类型的表单是相同的。FORM表单中填写的文本内容和文件上传字段中的文件路径名在内存中就是它们的某种字符集编码的字节数组形式,Apache文件上传组件在读取这些内容时,必须知道它们所采用的字符集编码,才能将它们转换成正确的字符文本返回。对于浏览器上传给WEB服务器的各个表单字段的描述头内容,Apache文件上传组件都需要将深入体验JavaWeb开发内幕—高级特性张孝祥著它们转换成字符串形式返回,setHeaderEncoding方法用于设置转换时所使用的字符集编码,其原理与笔者编著的《深入体验javaWeb开发内幕——核心基础》一书中的第6.9.4