13.8.3. 在表单中处理分段文件上传
在MultipartResolver完成分段文件解析后,这个请求就会和其它请求一样被处理。
为了使用文件上传,你需要创建一个带文件上传域(upload field)的(HTML)表单,让Spring将文件绑定到你的表单上(如下所示):
Upload a file pleasePlease upload a file
可以看到,在上面这个表单里有一个input元素,这个元素的名字(“file”)和服务器端处理这个表单的bean(在下面将会提到)中类型为byte[]的属性名相同。
在这个表单里我们也声明了编码参数(enctype="multipart/form-data")以便让浏览器知道如何对这个文件上传表单进行编码(千万不要忘记这么做!)。
和其它不能自动转为字符串类型或者基本类型(primitive type)的属性一样,为了将上传的二进制数据存成bean的属性,
必须通过ServletRequestDatabinder注册一个属性编辑器。
Spring中内置了几个这样的编辑器,它们可以处理文件,然后将结果存成bean的属性。
比如,StringMultipartEditor可以将文件转换成一个字符串(使用用户声明的字符集)。
ByteArrayMultipartEditor可以以将文件转换为byte数组。
他们的功能和CustomDateEditor相似。
总而言之,为了使用(HTML)表单上传文件,需要声明一个解析器,一个控制器,再将文件上传的URL映射到控制器来处理这个请求。
下面是这几个bean的声明。
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
/upload.form=fileUploadController
下面的代码定义了控制器和用来存放文件的那个bean。
public class FileUploadController extends SimpleFormController {
protected ModelAndView onSubmit(
HttpServletRequest request,
HttpServletResponse response,
Object command,
BindException errors) throws ServletException, IOException {
// 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
}
// well, let's do nothing with the bean for now and return
return super.onSubmit(request, response, command, errors);
}
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder)
throws ServletException {
// 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 them
}
}
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数组记录下来以外,没有对这个文件进行任何操作,
在实际的应用程序中你可以做任何你想做的事情(比如将文件存储在数据库中,通过电子邮件发送给某人等等)。
在下面这个例子里,上传的文件被绑定为(表单支持的)对象(form backing)的String属性:
public class FileUploadController extends SimpleFormController {
protected ModelAndView onSubmit(
HttpServletRequest request,
HttpServletResponse response,
Object command,
BindException errors) throws ServletException, IOException {
// 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
}
// well, let's do nothing with the bean for now and return
return super.onSubmit(request, response, command, errors);
}
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder)
throws ServletException {
// 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 object and convert them
}
}
public class FileUploadBean {
private String file;
public void setFile(String file) {
this.file = file;
}
public String getFile() {
return file;
}
}
如果仅仅是处理一个文本文件的上传,上面这个例子的做法还是合理的(但如果上传的是一张图片,
那段代码就会出问题)。
最后的解决方法就是将表单支持对象(form backing)的相关属性设成MultipartFile类型。
这样的话,没有类型转换的需要,我们也就不需要声明任何属性编辑器(PropertyEditor)。
public class FileUploadController extends SimpleFormController {
protected ModelAndView onSubmit(
HttpServletRequest request,
HttpServletResponse response,
Object command,
BindException errors) throws ServletException, IOException {
// 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
}
// well, let's do nothing with the bean for now and return
return super.onSubmit(request, response, command, errors);
}
}
public class FileUploadBean {
private MultipartFile file;
public void setFile(MultipartFile file) {
this.file = file;
}
public MultipartFile getFile() {
return file;
}
}