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属性会和其它的属性一样被处理。
任何已配置的PortletMultipartResolverbean必须
使用下列id(或名称):"PortletMultipartResolver" 。
如果你已经定义了你的PortletMultipartResolver为任何其他名称,
哪么DispatcherPortlet将找不到你的
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 {
// cast the bean
FileUploadBean bean = (FileUploadBean) command;
// let's see if there's content there
byte[] 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 byte[]
// we have to register a custom editor
binder.registerCustomEditor(byte[].class, new ByteArrayMultipartFileEditor());
// now Spring knows how to handle multipart object and convert
}
}
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;
}
}