使用JSF和MyFaces实现文件上载(2) | ||||||||
---|---|---|---|---|---|---|---|---|
http://www.sina.com.cn 2005年08月18日 08:48 天极yesky | ||||||||
文/陶刚编译 配置JSF和MyFaces扩展
org.apache.myfaces.component.html.util程序包中的MultipartRequestWrapper类充当MyFaces和通用文件上载之间的桥梁。这个类扩展了HttpServletRequestWrapper,重载了getParameterMap()、getParameterNames()、getParameter()和getParameterValues()方法,这样它们才能适应multipart/form-data编码方式。此外,MultipartRequestWrapper提供了两个新的方法,分别是getFileItem()和getFileItems(),它允许你通过org.apache.commons.fileupload.FileItem接口来访问被上载的文件。 当MyFaces检测到编码方式的时候,org.apache.myfaces.component.html.util程序包中的ExtensionsFilter就建立MultipartRequestWrapper实例。因此,你不需要关心窗体数据的分解,但是如果你希望改变被上载文件的处理方式,那么了解它是如何实现的就很有用处了。在典型的应用程序中,你必须在自己的web应用程序的web.xml描述信息中配置ExtensionsFilter,这样它才能在JSF的FacesServlet前面截取HTTP请求:
前面示例中的两个过滤器(filter)参数告诉MyFaces把小于100K的文件放入内存中,并且忽略(即不处理)占用10MB以上磁盘空间的文件。大小介于uploadThresholdSize和uploadMaxFileSize之间的文件将作为临时文件存储在磁盘上。如果你试图载入一个太大的文件,MyFaces当前版本将忽略所有的窗体数据,就像用户提交了一个空表单一样。如果你希望给上载文件失败的用户一些提示信息,就需要更改MyFaces的MultipartRequestWrapper类,找到捕捉SizeLimitExceededException异常的位置,并使用FacesContext.getCurrentInstance().addMessage()来警告用户。 前面提到,MyFaces扩展包含了文件上载组件,我们可以在JSF页面中使用它。下一部分将介绍如何实现这样的操作。 使用MyFaces的文件上载组件。 为了在web页面中使用JSF和MyFaces,你必须在<%@taglib%>指令中声明它们的标记库:
JSF的<h:form>标记没有method属性,这是因为它只支持POST方法,但它拥有enctype属性,如果你希望上载文件,就必须使用该属性,把窗体数据设置为多部分(multipart)编码类型:
MyFaces的<x:inputFileUpload>标记使你能够定义UI组件的属性,它的呈现部件(renderer)生成<input type="file">元素。org.apache.myfaces.custom.fileupload程序包包含了UI组件类(HtmlInputFileUpload)、呈现部件(HtmlFileUploadRenderer)、定制的标记处理程序(HtmlInputFileUploadTag)、UploadedFile接口和其它一些辅助类。HtmlInputFileUpload类扩展了JSF标准的HtmlInputText组件,重载了它的一些方法。HtmlFileUploadRenderer负责生成HTML标记,从MultipartRequestWrapper中获取FileItem。 MyFaces并没有让你直接访问通用文件上载所建立的FileItem实例,它提供了自己的UploadedFile接口来获取被上载的文件的内容、内容类型、文件名和文件大小。JSF窗体的后台bean必须拥有一个UploadedFile类型的属性。前面的例子用JSF表达式(#{myBean.myFile})把UI组件的值绑定到了这样一个bean上。JSF框架组件将获取HtmlInputFileUpload组件的值,它是一个UploadedFile实例,并且会设置后台bean的myFile属性:
MyFaces拥有UploadedFile接口的两种实现方式:UploadedFileDefaultMemoryImpl和UploadedFileDefaultFileImpl。当<x:inputFileUpload>标记没有带storage属性,或者这个属性的值是memory的时候,就使用前者。当storage属性的值是file的时候使用后者。 UploadedFileDefaultMemoryImpl类从FileItem实例中获取被上载文件的内容、文件名、文件大小和内容类型,并把所有这些信息存储在私有字段中。因此,即使通用文件上载把这个文件保存在磁盘上,UploadedFile的这种实现也会把被上载文件的内容保存在内存中,浪费了资源。 UploadedFileDefaultFileImpl类使用一个过渡字段来保存FileItem实例的指针,仅仅在调用getInputStream()方法的时候,才使用它来获取被上载文件的内容。这种实现节约了内存空间,但是,如果它是序列化的(serialize),那么在还原序列化(deserialization)之后,你就无法获取文件内容了。因此,文件上载窗体的后台bean不能保存在session作用域(scope)中,因为当应用程序重新启动或服务器关闭的时候,应用程序服务器会序列化对话(session)bean。 如果你希望得到了一个工作正常的、效率很高的解决方案,那么就把后台bean保存在request作用域中,并在<x:inputFileUpload>中指定storage="file"以节约内存资源。你也可以通过添加writeObject()方法(它用于序列化被上载文件的内容)来解决UploadedFileDefaultFileImpl类的序列化问题。为了保证UploadedFile的这种实现的高效性,相应的readObject()方法应该重新建立一个临时文件,而不是从内存中读取内容。 使用MyFaces的时候还需要注意一个问题:UploadedFile接口并没有定义一个用于删除通用文件上载在磁盘上所建立的临时文件的方法。当FileItem实例作为垃圾被清理的时候,就应该删掉这些文件。通用文件上载的DefaultFileItem类拥有一个finalize()方法,当管理临时文件的对象被从内存中删除的时候,它可以删除它们所管理的临时文件。如果你的应用程序正在上载大型文件的时候,你可能希望在它们被处理后立即删除,而不用等待垃圾清除过程。为了实现这样的功能,你必须添加一个getFileItem()方法(在UploadedFileDefaultFileImpl中),它应该返回FileItem实例,而该实例拥有delete()方法。
|