《 Spring in action》複習筆記
處理 multipart 形式的資料
配置 multipart 解析器 DispatcherServlet 沒有實現任何解析 multipart 請求資料的功能。它將該任務委託給了 MultipartResolver 策略藉口的實現。Spring 內建了兩個 MultipartResolver 的實現供我們選擇:
- CommonsMultipartResolver : 使用 Jakarta Commons FileUpload 解析
- StandardServletMultipartResolver: 依賴於 Servlet 3.0 對 multipart 請求的支援。
一般選用後者更優。它使用 Servlet 所提供的功能支援,並不需要依賴其他專案。 使用 Servlet 3.0 解析 multipart 請求
@Bean
public MultipartResolver multipartResolver() throws IOException {
return new StandardServletMultipartResolver();
}
想要限制其工作方式,要在 Servlet 中指定 multipart 的配置。具體來說必須要在 web.xml 或 Servlet 初始化類中,將 multipart 的具體細節作為 DispatcherServlet 配置的一部分。 如果之前採用了 Servlet 初始化類的方式配置 DispathcerServlet 的話,這個初始化類應該已經實現了 WebApplicationInitializer ,那麼可以在 Servlet registration 上呼叫 setMultipartConfig() 方法,傳入一個 MultipartConfig-Element 例項。
DispatcherServlet ds = new DispatcherServlet();
Dynamic registration = context.addServlet("appServlet",da);
registration.addMapping("/");
registration.setMultipartConfig(
new MultipartConfigElement("/tmp/iunote/uploads"));
如果我們配置 DispatcherServlet 的 Servlet 初始化類繼承了 AbstractAnnotationConfigDispatcherServletInitializer 或 AbstractDispatcherServletInitializer 的話,我們不會直接建立 DispatcherServlet 例項並將其註冊到 Servlet 上下文中。這樣,就不會有 Dynamic Servlet registration 的引用供我們使用。但是,可以通過過載 customizeRegistration() 方法( 會得到一個 Dynamic 作為引數 )來配置 multipart 的具體細節:
@Override
protected void customizeRegistration(Dynamic registration) {
registration.setMultipartConfig(
new MultipartConfigElement("/tmp/iunote/uploads"));
}
MultipartConfigElement 構造器可以接受的其他構造器:
- 上傳檔案的最大容量。預設沒有限制
- 整個 multipart 請求的最大容量(以位元組為單位),不會關心有多少個 part 以及每個 part 的大小。預設沒有限制
- 在上傳的過程中,如果檔案大小達到一個指定的最大容量,將會寫入到臨時檔案路徑中。預設值為 0 ,也就是所有檔案都會寫入到磁碟上。
想限制檔案大小不超過 2MB ,整個請求不超過 4MB,而且所有檔案都要寫入磁碟中的示例程式碼:
@Override
protected void customizeRegistration(Dynamic registration){
registration.setMultipartConfig(
new MultipartConfigElement("/tmp/iunote/uploads",
2097152, 4194304, 0));
}
使用傳統的 web.xml 來配置 MultipartConfigElement 可以使用 中的 元素,如下所示:
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframewordk.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<multipart-config>
<location>/tmp/iunote/uploads</location>
<max-file-size>2097152</max-file-size>
<max-request-size>4194304</max-request-size>
</multipart-config>
</servlet>
配置 Jakarta Commons FileUpload multipart 解析器
如果喜歡可以編寫自己的 MultipartResolver 實現,Spring 內建了 CommonsMultipartResolver 可以作為 StandardServletMultipartResolver 的替代方案。 將 CommonsMultipartResolver 宣告為 Spring bean 的最簡單方式如下:
@Bean
public MultipartResolver multipartResolver() {
return new CommonsMultipartResolver();
}
與 StandardServletMultipartResolver 有所不同, CommonsMultipartResolver 不會強制要求設定臨時檔案路徑。預設就是 Servlet 容器的臨時目錄。通過設定 uploadTempDir 屬性可以指定不同位置:
@Bean
public MultipartResolver multipartResolver() throws IOException {
CommonsMultipartResolver multipartResolver =
new CommonsMultipartResolver();
multipartResolver.setUploadTempDir(
new FileSystemResource("/tmp/iunote/uploads"));
multipartResolver.setMaxUploadSize(2097152);
multipartResolver.setMaxInMemorySize(0);
return multipartResolver;
}
與 MultipartConfigElement 不同,此處無法設定 multipart 請求整體的最大容量。
處理 multipart 請求
配置好了對 multipart 請求的處理,就可以編寫控制器方法來接收上傳的檔案。最常見的方式就是在某個控制器方法引數上新增 @RequestPart 註解。 首先需要修改表單,以允許使用者選擇要上傳的圖片,同時還要修改控制器方法。 首先是表單的修改:
<form method="POST" enctype="multipart/form-data">
<input type="file" name="picture" accept="image/jpeg,image/png,image/gif" />
</form>
然後是控制器方法的改寫:
@RequestMapping(value="/register", method=POST)
public String processRegistration {
@RequestPart("profilePicture") byte[] profilePiture,
@Valid Spitter spitter,
Errors errors){
...
}
接收 MultipartFile 使用上傳檔案的原始 byte 比較簡單但功能有限。因此 Spring 還提供了 MultipartFile 藉口。
public interface MultipartFile {
String getName() ;
String getOriginalFilename();
String getContentType();
boolean isEmpty();
byte[] getBytes() throws IOException;
InputStream getInputStream() throws IOException;
void transferTo(File dest) throws IOException;
}
可以看到提供了獲取上傳檔案 byte 的方式,還能獲得原始的檔名、大小以及內容型別。還提供了一個 InputStream 用來將檔案資料以流的方式進行讀取。 還提供了一個便利的 transferTo() 方法來將上傳的檔案寫入到檔案系統中。
profilePicture.transferTo(
new File("/data/spittr/" + profilePiture.getOriginalFIlename());
以 Part 的形式接收上傳的檔案 如果需要將應用部署到 Servlet 3.0 的容器中,有一個 MultipartFile 的一個替代方案。Spring MVC 也能接收 javax.servlet.http.Part 作為控制器方法的引數。如果使用 Part 來替換 MultipartFile 的話,那麼 processRegistration() 的方法簽名將做出相應的改變:
@RequestMapping(value="/register", method=POST)
public String processRegistration(
@RequestPart("profilePicture") Part profilePicture,
@Valid Spitter spitter,
Errors errors){
...
}
就主體而言,Part 藉口與 MultipartFile 並沒有太大的差別。
public interface Part {
public InputStream getInputStream() throws IOException;
public String getContentType();
public String getName();
public String getSubmittedFileName();
public long getSize();
public void write(String fileName) throws IOException;
public void delete() throws IOException;
public String getHeader(String name);
public Collection<String> getHeaders(String name);
public Collection<String> getHeaderNames();
}
getSubmittedFileName() 對應於 getOriginalFilename() write() 對應於 transferTo(). 如果編寫控制器方法的時候,通過 Part 引數的形式接受檔案上床,那麼就沒有必要配置 MultipartResolver 了,只有使用 MultipartFile 的時候,我們才需要 MultipartResolver 。