?
このドキュメントでは、 php中國(guó)語(yǔ)ネットマニュアル リリース
Spring支持web應(yīng)用中的分段文件上傳。這種支持是由即插即用的MultipartResolver
來(lái)實(shí)現(xiàn)。
這些解析器都定義在org.springframework.web.multipart
包里。
Spring提供了現(xiàn)成的MultipartResolver
可以支持Commons FileUpload(http://jakarta.apache.org/commons/fileupload)和
COS FileUpload(http://www.servlets.com/cos)。
本章后面的部分描述了Spring是如何支持文件上傳的。
通常情況下,Spring是不處理文件上傳的,因?yàn)橐恍╅_(kāi)發(fā)者想要自己處理它們。
如果想使用Spring的這個(gè)功能,需要在web應(yīng)用的上下文中添加分段文件解析器。
這樣,每個(gè)請(qǐng)求就會(huì)被檢查是否包含文件上傳。如果沒(méi)有,這個(gè)請(qǐng)求就被正常的處理,
否則,應(yīng)用上下文中已經(jīng)定義的MultipartResolver
就會(huì)被調(diào)用。
然后,請(qǐng)求中的文件屬性就會(huì)像其它屬性一樣被處理。
下面的例子說(shuō)明了如何使用CommonsMultipartResolver
:
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- one of the properties available; the maximum file size in bytes -->
<property name="maxUploadSize" value="100000"/>
</bean>
下面這個(gè)例子使用CosMultipartResolver
:
<bean id="multipartResolver" class="org.springframework.web.multipart.cos.CosMultipartResolver">
<!-- one of the properties available; the maximum file size in bytes -->
<property name="maxUploadSize" value="100000"/>
</bean>
當(dāng)然,需要在classpath中為分段文件解析器提供正確的jar文件。
如果是CommonsMultipartResolver
,
需要使用commons-fileupload.jar
,如果是CosMultipartResolver
,
則使用cos.jar
。
已經(jīng)看到如何設(shè)置Spring處理文件上傳請(qǐng)求,接下來(lái)我們看看如何使用它。
當(dāng)Spring的DispatcherServlet
發(fā)現(xiàn)文件上傳請(qǐng)求時(shí),它會(huì)激活定義在上下文中的解析器來(lái)處理請(qǐng)求。
這個(gè)解析器隨后是將當(dāng)前的HttpServletRequest
封裝成MultipartHttpServletRequest
,后者支持分段文件上傳。
使用MultipartHttpServletRequest
,可以獲取請(qǐng)求所包含的上傳信息,甚至可以在控制器中獲取分段文件的內(nèi)容。
在MultipartResolver
完成分段文件解析后,這個(gè)請(qǐng)求就會(huì)和其它請(qǐng)求一樣被處理。
為了使用文件上傳,你需要?jiǎng)?chuàng)建一個(gè)帶文件上傳域(upload field)的(HTML)表單,讓Spring將文件綁定到你的表單上(如下所示):
<html> <head> <title>Upload a file please</title> </head> <body> <h1>Please upload a file</h1> <form method="post" action="upload.form" enctype="multipart/form-data"> <input type="file" name="file"/> <input type="submit"/> </form> </body> </html>
可以看到,在上面這個(gè)表單里有一個(gè)input元素,這個(gè)元素的名字(“file”)和服務(wù)器端處理這個(gè)表單的bean(在下面將會(huì)提到)中類(lèi)型為byte[]
的屬性名相同。
在這個(gè)表單里我們也聲明了編碼參數(shù)(enctype="multipart/form-data"
)以便讓瀏覽器知道如何對(duì)這個(gè)文件上傳表單進(jìn)行編碼(千萬(wàn)不要忘記這么做!)。
和其它不能自動(dòng)轉(zhuǎn)為字符串類(lèi)型或者基本類(lèi)型(primitive type)的屬性一樣,為了將上傳的二進(jìn)制數(shù)據(jù)存成bean的屬性,
必須通過(guò)ServletRequestDatabinder
注冊(cè)一個(gè)屬性編輯器。
Spring中內(nèi)置了幾個(gè)這樣的編輯器,它們可以處理文件,然后將結(jié)果存成bean的屬性。
比如,StringMultipartEditor
可以將文件轉(zhuǎn)換成一個(gè)字符串(使用用戶(hù)聲明的字符集)。
ByteArrayMultipartEditor
可以以將文件轉(zhuǎn)換為byte數(shù)組。
他們的功能和CustomDateEditor
相似。
總而言之,為了使用(HTML)表單上傳文件,需要聲明一個(gè)解析器,一個(gè)控制器,再將文件上傳的URL映射到控制器來(lái)處理這個(gè)請(qǐng)求。 下面是這幾個(gè)bean的聲明。
<beans>
<!-- lets use the Commons-based implementation of the MultipartResolver interface -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/upload.form=fileUploadController
</value>
</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>
</beans>
下面的代碼定義了控制器和用來(lái)存放文件的那個(gè)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
用一個(gè)byte[]
類(lèi)型的屬性來(lái)存放文件。
前面已經(jīng)提到過(guò),通常控制器注冊(cè)一個(gè)自定義的編輯器以便讓Spring知道如何將解析器找到的multipart對(duì)象轉(zhuǎn)換成bean指定的屬性,
但在上面的例子中,我們除了將byte數(shù)組記錄下來(lái)以外,沒(méi)有對(duì)這個(gè)文件進(jìn)行任何操作,
在實(shí)際的應(yīng)用程序中你可以做任何你想做的事情(比如將文件存儲(chǔ)在數(shù)據(jù)庫(kù)中,通過(guò)電子郵件發(fā)送給某人等等)。
在下面這個(gè)例子里,上傳的文件被綁定為(表單支持的)對(duì)象(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; } }
如果僅僅是處理一個(gè)文本文件的上傳,上面這個(gè)例子的做法還是合理的(但如果上傳的是一張圖片, 那段代碼就會(huì)出問(wèn)題)。
最后的解決方法就是將表單支持對(duì)象(form backing)的相關(guān)屬性設(shè)成MultipartFile
類(lèi)型。
這樣的話(huà),沒(méi)有類(lèi)型轉(zhuǎn)換的需要,我們也就不需要聲明任何屬性編輯器(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; } }