收藏 分享(赏)

文件上传组件的应用与编写.doc

上传人:苏醒文档集 文档编号:19370535 上传时间:2023-03-14 格式:DOC 页数:50 大小:1.97MB
下载 相关 举报
文件上传组件的应用与编写.doc_第1页
第1页 / 共50页
文件上传组件的应用与编写.doc_第2页
第2页 / 共50页
文件上传组件的应用与编写.doc_第3页
第3页 / 共50页
亲,该文档总共50页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述

1、深入体验JavaWeb开发内幕高级特性 张孝祥著 http:/www.it315.org第1章 文件上传组件的应用与编写在许多Web站点应用中都需要为用户提供通过浏览器上传文档资料的功能,例如,上传邮件附件、个人相片、共享资料等。对文件上传功能,在浏览器端提供了较好的支持,只要将FORM表单的enctype属性设置为“multipart/form-data”即可;但在Web服务器端如何获取浏览器上传的文件,需要进行复杂的编程处理。为了简化和帮助Web开发人员接收浏览器上传的文件,一些公司和组织专门开发了文件上传组件。本章将详细介绍如何使用Apache文件上传组件,以及分析该组件源程序的设计思路

2、和实现方法。1.1 准备实验环境按下面的步骤为本章的例子程序建立运行环境:(1)在Tomcat 5.5.12的webapps目录中创建一个名为fileupload的子目录,并在fileupload目录中创建一个名为test.html的网页文件,在该文件中写上“这是test.html页面的原始内容!”这几个字符。(2)在webappsfileupload目录中创建一个名为WEB-INF的子目录,在WEB-INF目录中创建一个名为classes的子目录和一个web.xml文件,web.xml文件内容如下: (3)要使用Apache文件上传组件,首先需要安装Apache文件上传组件包。在webapp

3、sfileuploadWEB-INF目录中创建一个名为lib的子目录,然后从网址http:/jakarta.apache.org/commons/fileupload下载到Apache组件的二进制发行包,在本书的附带带光盘中也提供了该组件的二进制发行包,文件名为commons-fileupload-1.0.zip。从commons-fileupload-1.0.zip压缩包中解压出commons-fileupload-1.0.jar文件,将它放置进webappsfileuploadWEB-INFlib目录中,就完成了Apache文件上传组件的安装。(4)在webappsfileupload目录

4、中创建一个名为src的子目录,src目录用于放置本章编写的Java源程序。为了便于对Servlet源文件进行编译,在src目录中编写一个compile.bat批处理文件,如例程1-1所示。例程1-1 compile.batset PATH=C:jdk1.5.0_01bin;%path%set CLASSPATH=C:tomcat-5.5.12commonlibservlet-api.jar;C:tomcat-5.5.12webappsfileuploadWEB-INFlibcommons-fileupload-1.0.jar;%CLASSPATH%javac -d .WEB-INFclasse

5、s %1pause在compile.bat批处理文件中要注意将commons-fileupload-1.0.jar文件的路径加入到CLASSPATH环境变量中和确保编译后生成的class文件存放到webappsfileuploadWEB-INFclasses目录中,上面的CLASSPATH环境变量的设置值由于排版原因进行了换行,实际上不应该有换行。接着在src目录中为compile.bat文件创建一个快捷方式,以后只要在Windows资源管理器窗口中将Java源文件拖动到compile.bat文件的快捷方式上,就可以完成Java源程序的编译了。之所以要创建compile.bat文件的快捷方式,

6、是因为直接将Java源程序拖动到compile.bat批处理文件时,compile.bat批处理文件内编写的相对路径不被支持。创建完的fileupload目录中的文件结构如图1.1所示。图1.1(4)启动Tomcat,在本地计算机的浏览器地址栏中输入如下地址:http:/localhost:8080/fileupload/test.html验证浏览器能够成功到该网页文档。如果浏览器无法访问到该网页文档,请检查前面的操作步骤和改正问题,直到浏览器能够成功到该网页文档为止。(5)为了让/fileupload这个WEB应用程序能自动重新装载发生了修改的Servlet程序,需要修改Tomcat的ser

7、ver.xml文件,在该文件的元素中增加如下一个子元素:保存server.xml文件后,重新启动Tomcat。1.2 Apache文件上传组件的应用Java Web开发人员可以使用Apache文件上传组件来接收浏览器上传的文件,该组件由多个类共同组成,但是,对于使用该组件来编写文件上传功能的Java Web开发人员来说,只需要了解和使用其中的三个类:DiskFileUpload、FileItem和FileUploadException。这三个类全部位于mons.fileupload包中。1.2.1查看API文档在准备实验环境时获得的commons-fileupload-1.0.zip文件的解压

8、缩目录中可以看到一个docs的子目录,其中包含了Apache文件上传组件中的各个API类的帮助文档,从这个文档中可以了解到各个API类的使用帮助信息。打开文件上传组件API帮助文档中的index.html页面,在左侧分栏窗口页面中列出了文件上传组件中的各个API类的名称,在右侧分栏窗口页面的底部列出了一段示例代码,如图1.2所示。图1.2读者不需要逐个去阅读图1.2中列出的各个API类的帮助文档,而应该以图1.2中的示例代码为线索,以其中所使用到的类为入口点,按图索骥地进行阅读,对于示例代码中调用到的各个API类的方法则应重点掌握。1.2.2 DiskFileUpload类DiskFileUp

9、load类是Apache文件上传组件的核心类,应用程序开发人员通过这个类来与Apache文件上传组件进行交互。下面介绍DiskFileUpload类中的几个常用的重要方法。1setSizeMax方法setSizeMax方法用于设置请求消息实体内容的最大允许大小,以防止客户端故意通过上传特大的文件来塞满服务器端的存储空间,单位为字节。其完整语法定义如下:public void setSizeMax(longsizeMax)如果请求消息中的实体内容的大小超过了setSizeMax方法的设置值,该方法将会抛出FileUploadException异常。2setSizeThreshold方法Apach

10、e文件上传组件在解析和处理上传数据中的每个字段内容时,需要临时保存解析出的数据。因为Java虚拟机默认可以使用的内存空间是有限的(笔者测试不大于100M),超出限制时将会发生“java.lang.OutOfMemoryError”错误,如果上传的文件很大,例如上传800M的文件,在内存中将无法保存该文件内容,Apache文件上传组件将用临时文件来保存这些数据;但如果上传的文件很小,例如上传600个字节的文件,显然将其直接保存在内存中更加有效。setSizeThreshold方法用于设置是否使用临时文件保存解析出的数据的那个临界值,该方法传入的参数的单位是字节。其完整语法定义如下:public

11、void setSizeThreshold(intsizeThreshold) 3. setRepositoryPath方法setRepositoryPath方法用于设置setSizeThreshold方法中提到的临时文件的存放目录,这里要求使用绝对路径。其完整语法定义如下:public void setRepositoryPath(StringrepositoryPath)如果不设置存放路径,那么临时文件将被储存在java.io.tmpdir这个JVM环境属性所指定的目录中,tomcat 5.5.9将这个属性设置为了“/temp/”目录。4. parseRequest方法parseReque

12、st 方法是DiskFileUpload类的重要方法,它是对HTTP请求消息进行解析的入口方法,如果请求消息中的实体内容的类型不是“multipart/form-data”,该方法将抛出FileUploadException异常。parseRequest 方法解析出FORM表单中的每个字段的数据,并将它们分别包装成独立的FileItem对象,然后将这些FileItem对象加入进一个List类型的集合对象中返回。parseRequest 方法的完整语法定义如下:public List parseRequest(HttpServletRequestreq)parseRequest 方法还有一个重载

13、方法,该方法集中处理上述所有方法的功能,其完整语法定义如下:parseRequest(HttpServletRequestreq,intsizeThreshold,longsizeMax,Stringpath)这两个parseRequest方法都会抛出FileUploadException异常。5. isMultipartContent方法isMultipartContent方法方法用于判断请求消息中的内容是否是“multipart/form-data”类型,是则返回true,否则返回false。isMultipartContent方法是一个静态方法,不用创建DiskFileUpload类的实

14、例对象即可被调用,其完整语法定义如下:public static final boolean isMultipartContent(HttpServletRequestreq)6. setHeaderEncoding方法由于浏览器在提交FORM表单时,会将普通表单中填写的文本内容传递给服务器,对于文件上传字段,除了传递原始的文件内容外,还要传递其文件路径名等信息,如后面的图1.3所示。不管FORM表单采用的是“application/x-www-form-urlencoded”编码,还是“multipart/form-data”编码,它们仅仅是将各个FORM表单字段元素内容组织到一起的一种格式

15、,而这些内容又是由某种字符集编码来表示的。关于浏览器采用何种字符集来编码FORM表单字段中的内容,请参看笔者编著的深入体验java Web开发内幕核心基础一书中的第6.9.2的讲解,“multipart/form-data”类型的表单为表单字段内容选择字符集编码的原理和方式与“application/x-www-form-urlencoded”类型的表单是相同的。FORM表单中填写的文本内容和文件上传字段中的文件路径名在内存中就是它们的某种字符集编码的字节数组形式,Apache文件上传组件在读取这些内容时,必须知道它们所采用的字符集编码,才能将它们转换成正确的字符文本返回。对于浏览器上传给WE

16、B服务器的各个表单字段的描述头内容,Apache文件上传组件都需要将它们转换成字符串形式返回,setHeaderEncoding 方法用于设置转换时所使用的字符集编码,其原理与笔者编著的深入体验java Web开发内幕核心基础一书中的第6.9.4节讲解的ServletRequest.setCharacterEncoding方法相同。setHeaderEncoding 方法的完整语法定义如下:public void setHeaderEncoding(Stringencoding) 其中,encoding参数用于指定将各个表单字段的描述头内容转换成字符串时所使用的字符集编码。注意:如果读者在使用

17、Apache文件上传组件时遇到了中文字符的乱码问题,一般都是没有正确调用setHeaderEncoding方法的原因。1.2.3 FileItem类FileItem类用来封装单个表单字段元素的数据,一个表单字段元素对应一个FileItem对象,通过调用FileItem对象的方法可以获得相关表单字段元素的数据。FileItem是一个接口,在应用程序中使用的实际上是该接口一个实现类,该实现类的名称并不重要,程序可以采用FileItem接口类型来对它进行引用和访问,为了便于讲解,这里将FileItem实现类称之为FileItem类。FileItem类还实现了Serializable接口,以支持序列化

18、操作。对于“multipart/form-data”类型的FORM表单,浏览器上传的实体内容中的每个表单字段元素的数据之间用字段分隔界线进行分割,两个分隔界线间的内容称为一个分区,每个分区中的内容可以被看作两部分,一部分是对表单字段元素进行描述的描述头,另外一部是表单字段元素的主体内容,如图1.3所示。图 1.3主体部分有两种可能性,要么是用户填写的表单内容,要么是文件内容。FileItem类对象实际上就是对图1.3中的一个分区的数据进行封装的对象,它内部用了两个成员变量来分别存储描述头和主体内容,其中保存主体内容的变量是一个输出流类型的对象。当主体内容的大小小于DiskFileUpload.

19、setSizeThreshold方法设置的临界值大小时,这个流对象关联到一片内存,主体内容将会被保存在内存中。当主体内容的数据超过DiskFileUpload.setSizeThreshold方法设置的临界值大小时,这个流对象关联到硬盘上的一个临时文件,主体内容将被保存到该临时文件中。临时文件的存储目录由DiskFileUpload.setRepositoryPath方法设置,临时文件名的格式为“upload_00000005(八位或八位以上的数字).tmp”这种形式,FileItem类内部提供了维护临时文件名中的数值不重复的机制,以保证了临时文件名的唯一性。当应用程序将主体内容保存到一个指定

20、的文件中时,或者在FileItem对象被垃圾回收器回收时,或者Java虚拟机结束时,Apache文件上传组件都会尝试删除临时文件,以尽量保证临时文件能被及时清除。下面介绍FileItem类中的几个常用的方法:1. isFormField方法isFormField方法用于判断FileItem类对象封装的数据是否属于一个普通表单字段,还是属于一个文件表单字段,如果是普通表单字段则返回true,否则返回false。该方法的完整语法定义如下:public boolean isFormField()2. getName方法getName方法用于获得文件上传字段中的文件名,对于图1.3中的第三个分区所示的

21、描述头,getName方法返回的结果为字符串“C:bg.gif”。如果FileItem类对象对应的是普通表单字段,getName方法将返回null。即使用户没有通过网页表单中的文件字段传递任何文件,但只要设置了文件表单字段的name属性,浏览器也会将文件字段的信息传递给服务器,只是文件名和文件内容部分都为空,但这个表单字段仍然对应一个FileItem对象,此时,getName方法返回结果为空字符串,读者在调用Apache文件上传组件时要注意考虑这个情况。getName方法的完整语法定义如下:public String getName()注意:如果用户使用Windows系统上传文件,浏览器将传递

22、该文件的完整路径,如果用户使用Linux或者Unix系统上传文件,浏览器将只传递该文件的名称部分。3getFieldName方法getFieldName方法用于返回表单字段元素的name属性值,也就是返回图1.3中的各个描述头部分中的name属性值,例如“name=p1”中的“p1”。getFieldName方法的完整语法定义如下:public String getFieldName()4. write方法write方法用于将FileItem对象中保存的主体内容保存到某个指定的文件中。如果FileItem对象中的主体内容是保存在某个临时文件中,该方法顺利完成后,临时文件有可能会被清除。该方法也

23、可将普通表单字段内容写入到一个文件中,但它主要用途是将上传的文件内容保存在本地文件系统中。其完整语法定义如下:public void write(Filefile)5getString方法getString方法用于将FileItem对象中保存的主体内容作为一个字符串返回,它有两个重载的定义形式:public java.lang.String getString()public java.lang.String getString(java.lang.Stringencoding)throws java.io.UnsupportedEncodingException前者使用缺省的字符集编码将主体

24、内容转换成字符串,后者使用参数指定的字符集编码将主体内容转换成字符串。如果在读取普通表单字段元素的内容时出现了中文乱码现象,请调用第二个getString方法,并为之传递正确的字符集编码名称。6. getContentType方法getContentType 方法用于获得上传文件的类型,对于图1.3中的第三个分区所示的描述头,getContentType方法返回的结果为字符串“image/gif”,即“Content-Type”字段的值部分。如果FileItem类对象对应的是普通表单字段,该方法将返回null。getContentType 方法的完整语法定义如下:public String g

25、etContentType()7. isInMemory方法isInMemory方法用来判断FileItem类对象封装的主体内容是存储在内存中,还是存储在临时文件中,如果存储在内存中则返回true,否则返回false。其完整语法定义如下:public boolean isInMemory()8. delete方法delete方法用来清空FileItem类对象中存放的主体内容,如果主体内容被保存在临时文件中,delete方法将删除该临时文件。尽管Apache组件使用了多种方式来尽量及时清理临时文件,但系统出现异常时,仍有可能造成有的临时文件被永久保存在了硬盘中。在有些情况下,可以调用这个方法来及

26、时删除临时文件。其完整语法定义如下:public void delete()1.2.4 FileUploadException类在文件上传过程中,可能发生各种各样的异常,例如网络中断、数据丢失等等。为了对不同异常进行合适的处理,Apache文件上传组件还开发了四个异常类,其中FileUploadException是其他异常类的父类,其他几个类只是被间接调用的底层类,对于Apache组件调用人员来说,只需对FileUploadException异常类进行捕获和处理即可。1.2.5 文件上传编程实例下面参考图1.2中看到的示例代码编写一个使用Apache文件上传组件来上传文件的例子程序。:动手体验

27、:使用Apache文件上传组件(1)在webappsfileupload目录中按例程1-1编写一个名为FileUpload.html的HTML页面,该页面用于提供文件上传的FORM表单,表单的enctype属性设置值为“multipart/form-data”,表单的action属性设置为“servlet/UploadServlet”。例程1-1 FileUpload.htmlupload experiment测试文件上传组件的页面 作者: 来自: 文件1:文件2:(2)在webappsfileuploadsrc目录中按例程1-2创建一个名为UploadServlet.java的Servlet

28、程序,UploadServlet.java调用Apache文件上传组件来处理FORM表单提交的文件内容和普通字段数据。例程1-2 UploadServlet.javaimport java.io.*;import javax.servlet.*;import javax.servlet.http.*;import mons.fileupload.*;import java.util.*;public class UploadServlet extends HttpServletpublic void doPost(HttpServletRequest request,HttpServletRe

29、sponse response) throws ServletException,IOExceptionresponse.setContentType(text/html;charset=gb2312);PrintWriter out = response.getWriter(); /设置保存上传文件的目录String uploadDir = getServletContext().getRealPath(/upload);if (uploadDir = null)out.println(无法访问存储目录!);return;File fUploadDir = new File(uploadDi

30、r);if(!fUploadDir.exists()if(!fUploadDir.mkdir()out.println(无法创建存储目录!);return; if (!DiskFileUpload.isMultipartContent(request) out.println(只能处理multipart/form-data类型的数据!);return ; DiskFileUpload fu = new DiskFileUpload();/最多上传200M数据fu.setSizeMax(1024 * 1024 * 200);/超过1M的字段数据采用临时文件缓存fu.setSizeThreshol

31、d(1024 * 1024);/采用默认的临时文件存储位置/fu.setRepositoryPath(.);/设置上传的普通字段的名称和文件字段的文件名所采用的字符集编码fu.setHeaderEncoding(gb2312);/得到所有表单字段对象的集合List fileItems = null;tryfileItems = fu.parseRequest(request);catch (FileUploadException e) out.println(解析数据时出现如下问题:);e.printStackTrace(out);return;/处理每个表单字段Iterator i = fi

32、leItems.iterator();while (i.hasNext() FileItem fi = (FileItem) i.next();if (fi.isFormField() String content = fi.getString(GB2312);String fieldName = fi.getFieldName();request.setAttribute(fieldName,content); elsetry String pathSrc = fi.getName();/*如果用户没有在FORM表单的文件字段中选择任何文件,那么忽略对该字段项的处理*/if(pathSrc.

33、trim().equals()continue;int start = pathSrc.lastIndexOf();String fileName = pathSrc.substring(start + 1);File pathDest = new File(uploadDir, fileName); fi.write(pathDest);String fieldName = fi.getFieldName();request.setAttribute(fieldName, fileName);catch (Exception e) out.println(存储文件时出现如下问题:); e.p

34、rintStackTrace(out); return;finally /总是立即删除保存表单字段内容的临时文件fi.delete();/显示处理结果out.println(用户: + request.getAttribute(author) + );out.println(来自: + request.getAttribute(company) + );/*将上传的文件名组合成file1,file2这种形式显示出来,如果没有上传 *任何文件,则显示为无,如果只上传了第二个文件,显示为file2。*/StringBuffer filelist = new StringBuffer();Strin

35、g file1 = (String)request.getAttribute(file1);makeUpList(filelist,file1);String file2 = (String)request.getAttribute(file2);makeUpList(filelist,file2);out.println(成功上传的文件: + (filelist.length()=0 ? 无 : filelist.toString();/* *将一段字符串追加到一个结果字符串中。如果结果字符串的初始内容不为空, *在追加当前这段字符串之前先最加一个逗号(,)。在组合sql语句的查询条件时,

36、*经常要用到类似的方法,第一条件前没有and,而后面的条件前都需要用and *作连词,如果没有选择第一个条件,第二个条件就变成第一个,依此类推。 * *param result 要将当前字符串追加进去的结果字符串 *param fragment 当前要追加的字符串 */private void makeUpList(StringBuffer result,String fragment)if(fragment != null)if(result.length() != 0)result.append(,);result.append(fragment);在Windows资源管理器窗口中将Upl

37、oadServlet.java源文件拖动到compile.bat文件的快捷方式上进行编译,修改Javac编译程序报告的错误,直到编译成功通过为止。(3)修改webappsfileuploadWEB-INFclassesweb.xml文件,在其中注册和映射UploadServlet的访问路径,如例程1-3所示。例程1-3 web.xmlUploadServletUploadServlet UploadServlet/servlet/UploadServlet (4)重新启动Tomcat,并在浏览器地址栏中输入如下地址:http:/localhost:8080/fileupload/FileUpl

38、oad.html填写返回页面中的FORM表单,如图1.4所示,单击“上载”按钮后,浏览器返回的页面信息如图1.5所示。图1.4图1.5(这些图的标题栏中的it315改为fileupload)查看webappsit315upload目录,可以看到刚才上传的两个文件。(4)单击浏览器工具栏上的“后退”按钮回到表单填写页面,只在第二个文件字段中选择一个文件,单击“上载”按钮,浏览器返回的显示结果如图1.6所示。图1.6M脚下留心:上面编写的Servlet程序将上传的文件保存在了当前WEB应用程序下面的upload目录中,这个目录是客户端浏览器可以访问到的目录。如果用户通过浏览器上传了一个名称为tes

39、t.jsp的文件,那么用户接着就可以在浏览器中访问这个test.jsp文件了,对于本地浏览器来说,这个jsp文件的访问URL地址如下所示:http:/localhost:8080/fileupload/upload/test.jsp对于远程客户端浏览器而言,只需要将上面的url地址中的localhost改写为Tomcat服务器的主机名或IP地址即可。用户可以通过上面的Servlet程序来上传自己编写的jsp文件,然后又可以通过浏览器来访问这个jsp文件,如果用户在jsp文件中编写一些有害的程序代码,例如,查看服务器上的所有目录结构,调用服务器上的操作系统进程等等,这将是一个非常致命的安全漏洞和

40、隐患,这台服务器对外就没有任何安全性可言了。 1.3 Apache文件上传组件的源码赏析经常阅读一些知名的开源项目的源代码,可以帮助我们开阔眼界和快速提高编程能力。Apache文件上传组件是Apache组织开发的一个开源项目,从网址http:/jakarta.apache.org/commons/fileupload可以下载到Apache组件的源程序包,在本书的附带带光盘中也提供了该组件的源程序包,文件名为commons-fileupload-1.0-src.zip。该组件的设计思想和程序编码细节包含有许多值得借鉴的技巧,为了便于有兴趣的读者学习和研究该组件的源码,本节将分析Apache文件上

41、传组件的源代码实现。对于只想了解如何使用Apache文件上传组件来上传文件的读者来说,不必学习本节的内容。在学习本节内容之前,读者需要仔细学习了笔者编著的深入体验java Web开发内幕核心基础一书中的第6.7.2节中讲解的“分析文件上传的请求消息结构”的知识。1.3.1 Apache文件上传组的类工作关系Apache文件上传组件总共由两个接口,十二个类组成。在Apache文件上传组件的十二个类中,有两个抽象类,四个的异常类,六个主要类,其中FileUpLoad类用暂时没有应用,是为了以后扩展而保留的。Apache文件上传组件中的各个类的关系如图1.7所示,图中省略了异常类。图 1.7Disk

42、FileUpload类是文件上传组件的核心类,它是一个总的控制类,首先由Apache文件上传组件的使用者直接调用DiskFileUpload类的方法,DiskFileUpload类再调用和协调更底层的类来完成具体的功能。解析类MultipartStream和工厂类DefaultFileItemFactory就是DiskFileUpload类调用的两个的底层类。MultipartStream类用于对请求消息中的实体数据进行具体解析,DefaultFileItemFactory类对MultipartStream类解析出来的数据进行封装,它将每个表单字段数据封装成一个个的FileItem类对象,用户通过FileItem类对象来获得相关表单字段的数据。DefaultFileItem是FileItem接口的实现类,实现了FileItem接口中定义的功能,用户只需关心FileItem接口,通过FileItem接口来使用DefaultFileItem类实现的功能。DefaultFileItem类使用了两个成员变量来分别存储表单字段数据的描述头和主体内容,其中保存主体内容的变量类型为DeferredFileOutputStream类。DeferredFileOutputStream类是一个输出流类型,在开始时,Defer

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 网络科技 > 开发文档

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报