1. 程式人生 > >《 Spring in action》複習筆記

《 Spring in action》複習筆記

處理 multipart 形式的資料

配置 multipart 解析器 DispatcherServlet 沒有實現任何解析 multipart 請求資料的功能。它將該任務委託給了 MultipartResolver 策略藉口的實現。Spring 內建了兩個 MultipartResolver 的實現供我們選擇:

  • CommonsMultipartResolver : 使用 Jakarta Commons FileUpload 解析
  • StandardServletMultipartResolver: 依賴於 Servlet 3.0 對 multipart 請求的支援。

一般選用後者更優。它使用 Servlet 所提供的功能支援,並不需要依賴其他專案。 使用 Servlet 3.0 解析 multipart 請求

StandardServletMultipartResolver 沒有構造器引數,也沒有要設定的屬性。宣告如下:

@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 。