17.7.Multipart文件上传支持
17.7. Multipart文件上传支持
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属性会和其它的属性一样被处理。
17.7.1. 使用PortletMultipartResolver
下面的例子介绍了 CommonsPortletMultipartResolver
的使用:
<bean id="portletMultipartResolver" > <!-- 一个属性;以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
里。
17.7.2. 处理表单里的文件上传
在 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" /> <bean id="portletModeHandlerMapping" > <property name="portletModeMap"> <map> <entry key="view" value-ref="fileUploadController"/> </map> </property> </bean> <bean id="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; } }