Spring Portlet MVC和Web MVC一样,也支持multipart来处理portlet中的文件上传。
插件式的PortletMultipartResolver
提供了对multipart的支持,
它在org.springframework.web.portlet.multipart
包里。
Spring提供了PortletMultipartResolver
来和
Commons FileUpload
一起使用。余下的篇幅会介绍文件上传的支持。
缺省情况下,Spring Portlet是不会处理multipart的,如果开发人员需要处理multipart,
就必须在web应用的context里添加一个multipart解析器,然后,
DispatcherPortlet
会在每个请求里检查是否带有multipart。
如果没找到,请求会继续,如果找到了multipart,在context中声明的
PortletMultipartResolver
会被调用。接着,
在请求里的multipart属性会和其它的属性一样被处理。
下面的例子介绍了
CommonsPortletMultipartResolver
的使用:
<bean id="portletMultipartResolver"
class="org.springframework.web.portlet.multipart.CommonsPortletMultipartResolver">
<!-- 一个属性;以byte为单位的最大文件长度 -->
<property name="maxUploadSize" value="100000"/>
</bean>
当然为了使multipart解析器能够工作,必须把合适的jar放到类路径里。对于
CommonsMultipartResolver
来说,需要
commons-fileupload.jar
。注意,必须使用至少1.1
版本的Commons FileUpload,因为以前的版本不支持JSR-168应用。
现在你已经看到如何设置Portlet MVC来处理multipart请求,接下来我们
讨论它的使用。当DispatcherPortlet
检测到
multipart时,它会激活在context里声明的解析器,并把请求交给它。然后解析器
把当前的ActionRequest
放到支持文件上传的
MultipartActionRequest
中。通过
MultipartActionRequest
,可以得到
请求包含的multipart信息,并且在控制器里访问multipart文件。
注意,不能从RenderRequest
接收到multipart
文件,而只能从ActionRequest
里。
在
PortletMultipartResolver
处理完后,
请求会继续被处理。你需要创建一个带有上传字段的表单来使用它(见下面),Spring会
把文件绑定在你的表单上(支持对象)。为了让用户上传文件,必须创建一个
(JSP/HTML)的表单:
<h1>Please upload a file</h1> <form method="post" action="<portlet:actionURL/>" enctype="multipart/form-data"> <input type="file" name="file"/> <input type="submit"/> </form>
如你所见,我们在bean的属性后面创建名为“File”的字段
用来容纳byte[]
。加上了编码属性(enctype="multipart/form-data"
),
让浏览器知道怎样来编码multipart字段(不要忘记!)。
和其它那些不会自动转化为字符串或原始类型的属性一样,为了把二进制数据放到对象
里,必须注册一个使用PortletRequestDataBinder
的自定义的编辑器。现成有好几个编辑器可以用来处理文件并把结果放到对象上。
StringMultipartFileEditor
能够把文件转换成字符串
(使用用户定义的字符集),ByteArrayMultipartFileEditor
能够把文件转换成字节数据。他们的功能和
CustomDateEditor
一样。
所以,为了能够使用表单来上传文件,需要声明解析器,映射到处理这个bean的控制器的 映射以及控制器。
<bean id="portletMultipartResolver" class="org.springframework.web.portlet.multipart.CommonsPortletMultipartResolver"/> <bean id="portletModeHandlerMapping" class="org.springframework.web.portlet.handler.PortletModeHandlerMapping"> <property name="portletModeMap"> <map> <entry key="view" value-ref="fileUploadController"/> </map> </property> </bean> <bean id="fileUploadController" class="examples.FileUploadController"> <property name="commandClass" value="examples.FileUploadBean"/> <property name="formView" value="fileuploadform"/> <property name="successView" value="confirmation"/> </bean>
接着,创建控制器以及实际容纳这个文件属性的类。
public class FileUploadController extends SimpleFormController { public void onSubmitAction( ActionRequest request, ActionResponse response, Object command, BindException errors) throws Exception { // 类型转换bean FileUploadBean bean = (FileUploadBean) command; // 是否有内容 byte[] file = bean.getFile(); if (file == null) { // 奇怪,用户什么都没有上传 } // do something with the file here } protected void initBinder( PortletRequest request, PortletRequestDataBinder binder) throws Exception { // to actually be able to convert Multipart instance to byte[] // we have to register a custom editor binder.registerCustomEditor(byte[].class, new ByteArrayMultipartFileEditor()); // 现在Spring知道如何来处理和转换multipart对象 } } public class FileUploadBean { private byte[] file; public void setFile(byte[] file) { this.file = file; } public byte[] getFile() { return file; } }
如你所见,FileUploadBean
有一个类型是
byte[]
的属性来容纳文件。控制器注册了一个自定义编辑器来
让Spring知道如何把解析器发现的multipart转换成指定的属性。在这个例子里,
没有对bean的byte[]
属性进行任何操作,但实际上,你可以做任
何操作(把它存到数据库里,把它电邮出去,或其它)
下面是一个例子,文件直接绑定在的一个(表单支持)对象上的字符串类型属性上面:
public class FileUploadController extends SimpleFormController { public void onSubmitAction( ActionRequest request, ActionResponse response, Object command, BindException errors) throws Exception { // cast the bean FileUploadBean bean = (FileUploadBean) command; // let's see if there's content there String file = bean.getFile(); if (file == null) { // hmm, that's strange, the user did not upload anything } // do something with the file here } protected void initBinder( PortletRequest request, PortletRequestDataBinder binder) throws Exception { // to actually be able to convert Multipart instance to a String // we have to register a custom editor binder.registerCustomEditor(String.class, new StringMultipartFileEditor()); // now Spring knows how to handle multipart objects and convert } } public class FileUploadBean { private String file; public void setFile(String file) { this.file = file; } public String getFile() { return file; } }
当然,最后的例子在上传文本文件时才有(逻辑上的)意义(在上传图像文件时, 它不会工作)。
第三个(也是最后一个)选项是,什么情况下需要直接绑定在(表单支持)对象的
MultipartFile
属性上。在以下的情况,
不需要注册自定义的属性编辑器,因为不需要类型转换。
public class FileUploadController extends SimpleFormController { public void onSubmitAction( ActionRequest request, ActionResponse response, Object command, BindException errors) throws Exception { // cast the bean FileUploadBean bean = (FileUploadBean) command; // let's see if there's content there MultipartFile file = bean.getFile(); if (file == null) { // hmm, that's strange, the user did not upload anything } // do something with the file here } } public class FileUploadBean { private MultipartFile file; public void setFile(MultipartFile file) { this.file = file; } public MultipartFile getFile() { return file; } }