Spring同樣支持文件上傳功能,不過該功能默認未開啟,因為可能有些開發者可能希望自己處理文件上傳過程。Spring的文件上傳功能在org.springframework.web.multipart
包下,有兩個MultipartResolver
實現用來支持文件上傳功能,一個是基于Commons FileUpload ,另一個基于Servlet 3.0 multipart請求解析功能。這兩個MultipartResolver
差不多,一個需要添加Commons FileUpload
的依賴,另一個需要在Servlet 3.0容器上運行。大家可以根據需要選擇。
定義MultipartResolver
使用Commons FileUpload MultipartResolver
在配置文件中添加如下一段,我們可以在Bean定義中配置上傳文件大小等屬性。
<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>
使用Servlet 3.0 MultipartResolver
由于使用的是Servlet API提供的文件上傳功能,所以文件大小等配置需要在web.xml
中進行配置。我們需要在dispathcer-servlet中添加<multipart-config>
標簽,它有四個子標簽來設置文件上傳的屬性。
這四個屬性如下:
- location ,臨時文件的存放位置,這個路徑必須是絕對路徑。
- fileSizeThreshold,文件起始值,大于該值文件才會被臨時保存,單位是字節。
- MaxFileSize,單個文件的最大值,單位是字節,不管上傳幾個文件,只要有一個文件大小超過該值就會拋出
IllegalStateException
。 - maxRequestSize,文件上傳請求的最大值,單位是字節,主要作用是當上傳多個文件是配置整個請求的大小,當超出該值是拋出
IllegalStateException
。
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
<multipart-config>
<max-file-size>100000</max-file-size>
</multipart-config>
</servlet>
然后我們在Spring配置文件中添加Servlet 3.0 MultipartResolver。
<bean id="multipartResolver"
class="org.springframework.web.multipart.support.StandardServletMultipartResolver">
</bean>
獲取文件
配置好了Multipart解析器之后,我們就可以接收文件了。首先定義一個頁面fileupload.jsp
,用于上傳文件并顯示服務器中的文件。注意在表單中我們必須添加enctype="multipart/form-data"
才能正確的上傳文件。
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>文件上傳</title>
<meta charset="utf-8">
</head>
<body>
<h2>文件上傳</h2>
<form action="<c:url value="/fileupload"/>"
method="post" enctype="multipart/form-data">
<label for="file">文件</label>
<input type="file" name="file" id="file"/>
<br>
<input type="submit" value="提交">
</form>
<h2>文件下載</h2>
<c:forEach var="file" items="${files}">
<a href="<c:url value="/findFile?filename=${file}"/>">${file}</a>
<br>
</c:forEach>
</body>
</html>
然后就可以在控制器中獲取文件了。由于MultipartFile
和它對應的臨時文件會在方法結束之后被Spring清除,所以我們必須在方法中將文件保存到合適的地方。這里我定義了一個UserFile
類將文件保存到Session中。
public class UserFile {
private String filename;
private byte[] bytes;
}
然后就是控制器了。在請求方法中,我們可以像普通參數那樣獲取上傳的文件,只不過文件對應的類型是MultipartFile
,如果使用的是Servlet 3.0標準的,那么類型還可以是javax.servlet.http.Part
。我寫了兩個處理方法,第一個將MultipartFile
轉化為上面的類型,然后保存到Session中。第二個方法用于獲取Session中的文件。
@Controller
public class FileUploadController {
@RequestMapping("/fileupload")
public String fileUpload(HttpSession session, @RequestParam(required = false) MultipartFile file, Model model) throws IOException {
List<UserFile> files = (List<UserFile>) session.getAttribute("files");
if (files == null)
files = new ArrayList<>();
if (file != null) {
UserFile f = new UserFile();
f.setFilename(file.getOriginalFilename());
f.setBytes(file.getBytes());
files.add(f);
}
session.setAttribute("files", files);
List<String> filenames = files.stream()
.map(UserFile::getFilename)
.collect(Collectors.toList());
model.addAttribute("files", filenames);
return "fileupload";
}
@RequestMapping("/findFile")
public void findFile(HttpSession session, @RequestParam String filename, HttpServletResponse response) throws IOException {
List<UserFile> files = (List<UserFile>) session.getAttribute("files");
Optional<UserFile> file = files.stream()
.filter(o -> Objects.equals(o.getFilename(), filename))
.findFirst();
OutputStream out = response.getOutputStream();
out.write(file.get().getBytes());
}
}