目錄
利用SpirngBoot實現檔案上傳功能
零、本篇要點
- 介紹SpringBoot對檔案上傳的自動配置。
- 介紹MultipartFile介面。
- 介紹SpringBoot+Thymeleaf檔案上傳demo的整合。
- 介紹對檔案型別,檔名長度等判斷方法。
一、SpringBoot對檔案處理相關自動配置
自動配置是SpringBoot為我們提供的便利之一,開發者可以在不作任何配置的情況下,使用SpringBoot提供的預設設定,如處理檔案需要的MultipartResolver。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, StandardServletMultipartResolver.class, MultipartConfigElement.class })
@ConditionalOnProperty(prefix = "spring.servlet.multipart", name = "enabled", matchIfMissing = true)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(MultipartProperties.class)
public class MultipartAutoConfiguration {
private final MultipartProperties multipartProperties;
public MultipartAutoConfiguration(MultipartProperties multipartProperties) {
this.multipartProperties = multipartProperties;
}
@Bean
@ConditionalOnMissingBean({ MultipartConfigElement.class, CommonsMultipartResolver.class })
public MultipartConfigElement multipartConfigElement() {
return this.multipartProperties.createMultipartConfig();
}
@Bean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
@ConditionalOnMissingBean(MultipartResolver.class)
public StandardServletMultipartResolver multipartResolver() {
StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
return multipartResolver;
}
}
- Spring3.1之後支援
StandardServletMultipartResolver
,且預設使用StandardServletMultipartResolver
,它的優點在於:使用Servlet所提供的功能支援,不需要依賴任何其他的專案。 - 想要自動配置生效,需要配置
spring.servlet.multipart.enabled=true
,當然這個配置預設就是true。 - 相關的配置設定在
MultipartProperties
中,其中欄位就是對應的屬性設定,經典欄位有:enabled
:是否開啟檔案上傳自動配置,預設開啟。location
:上傳檔案的臨時目錄。maxFileSize
:最大檔案大小,以位元組為單位,預設為1M。maxRequestSize
:整個請求的最大容量,預設為10M。fileSizeThreshold
:檔案大小達到該閾值,將寫入臨時目錄,預設為0,即所有檔案都會直接寫入磁碟臨時檔案中。resolveLazily
:是否惰性處理請求,預設為false。
- 我們也可以自定義處理的細節,需要實現MultipartResolver介面。
二、處理上傳檔案MultipartFile介面
SpringBoot為我們提供了MultipartFile強大介面,讓我們能夠獲取上傳檔案的詳細資訊,如原始檔名,內容型別等等,介面內容如下:
public interface MultipartFile extends InputStreamSource {
String getName(); //獲取引數名
@Nullable
String getOriginalFilename();//原始的檔名
@Nullable
String getContentType();//內容型別
boolean isEmpty();
long getSize(); //大小
byte[] getBytes() throws IOException;// 獲取位元組陣列
InputStream getInputStream() throws IOException;//以流方式進行讀取
default Resource getResource() {
return new MultipartFileResource(this);
}
// 將上傳的檔案寫入檔案系統
void transferTo(File var1) throws IOException, IllegalStateException;
// 寫入指定path
default void transferTo(Path dest) throws IOException, IllegalStateException {
FileCopyUtils.copy(this.getInputStream(), Files.newOutputStream(dest));
}
}
三、SpringBoot+Thymeleaf整合demo
1、編寫控制器
/**
* 檔案上傳
*
* @author Summerday
*/
@Controller
public class FileUploadController {
private static final String UPLOADED_FOLDER = System.getProperty("user.dir");
@GetMapping("/")
public String index() {
return "file";
}
@PostMapping("/upload")
public String singleFileUpload(@RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes) throws IOException {
if (file.isEmpty()) {
redirectAttributes.addFlashAttribute("msg", "檔案為空,請選擇你的檔案上傳");
return "redirect:uploadStatus";
}
saveFile(file);
redirectAttributes.addFlashAttribute("msg", "上傳檔案" + file.getOriginalFilename() + "成功");
redirectAttributes.addFlashAttribute("url", "/upload/" + file.getOriginalFilename());
return "redirect:uploadStatus";
}
private void saveFile(MultipartFile file) throws IOException {
Path path = Paths.get(UPLOADED_FOLDER + "/" + file.getOriginalFilename());
file.transferTo(path);
}
@GetMapping("/uploadStatus")
public String uploadStatus() {
return "uploadStatus";
}
}
2、編寫頁面file.html
<html xmlns:th="http://www.thymeleaf.org">
<!--suppress ALL-->
<html lang="en">
<head>
<meta charset="UTF-8">
<title>檔案上傳介面</title>
</head>
<body>
<div>
<form method="POST" enctype="multipart/form-data" action="/upload">
<table>
<tr><td><input type="file" name="file" /></td></tr>
<tr><td></td><td><input type="submit" value="上傳" /></td></tr>
</table>
</form>
</div>
</body>
</html>
3、編寫頁面uploadStatus.html
<!--suppress ALL-->
<html xmlns:th="http://www.thymeleaf.org">
<html lang="en">
<head>
<meta charset="UTF-8">
<title>檔案上傳介面</title>
</head>
<body>
<div th:if="${msg}">
<h2 th:text="${msg}"/>
</div>
<div >
<img src="" th:src="${url}" alt="">
</div>
</body>
</html>
4、編寫配置
server.port=8081
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
5、配置虛擬路徑對映
這一步是非常重要的,我們將檔案上傳到伺服器上時,我們需要將我們的請求路徑和伺服器上的路徑進行對應,不然很有可能檔案上傳成功,但訪問失敗:
@Configuration
public class MvcConfig implements WebMvcConfigurer {
private static final String UPLOADED_FOLDER = System.getProperty("user.dir");
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/upload/**")
.addResourceLocations("file:///" + UPLOADED_FOLDER + "/");
}
}
對應關係需要自己去定義,如果訪問失敗,可以試著列印以下路徑,看看是否缺失了路徑分隔符。
注意:如果addResourceHandler不要寫成處理/**,這樣會攔截掉其他的請求
6、測試頁面
執行mvn spring-boot:run
,啟動程式,訪問http://localhost:8081/
,選擇檔案,點選上傳按鈕,我們的專案目錄下出現了mongo.jpg,並且頁面也成功顯示:
四、SpringBoot的Restful風格,返回url
/**
* 檔案上傳
*
* @author Summerday
*/
@RestController
public class FileUploadRestController {
/**
* 檔名長度
*/
private static final int DEFAULT_FILE_NAME_LENGTH = 100;
/**
* 允許的檔案型別
*/
private static final String[] ALLOWED_EXTENSIONS = {
"jpg", "img", "png", "gif"
};
/**
* 專案路徑
*/
private static final String UPLOADED_FOLDER = System.getProperty("user.dir");
@PostMapping("/restUpload")
public Map<String,Object> singleFileUpload(@RequestParam("file") MultipartFile file) throws Exception {
if (file.isEmpty()) {
throw new Exception("檔案為空!");
}
String filename = upload(file);
String url = "/upload/" + filename;
Map<String,Object> map = new HashMap<>(2);
map.put("msg","上傳成功");
map.put("url",url);
return map;
}
/**
* 上傳方法
*/
private String upload(MultipartFile file) throws Exception {
int len = file.getOriginalFilename().length();
if (len > DEFAULT_FILE_NAME_LENGTH) {
throw new Exception("檔名超出限制!");
}
String extension = getExtension(file);
if(!isValidExtension(extension)){
throw new Exception("檔案格式不正確");
}
// 自定義檔名
String filename = getPathName(file);
// 獲取file物件
File desc = getFile(filename);
// 寫入file
file.transferTo(desc);
return filename;
}
/**
* 獲取file物件
*/
private File getFile(String filename) throws IOException {
File file = new File(UPLOADED_FOLDER + "/" + filename);
if(!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
if(!file.exists()){
file.createNewFile();
}
return file;
}
/**
* 驗證檔案型別是否正確
*/
private boolean isValidExtension(String extension) {
for (String allowedExtension : ALLOWED_EXTENSIONS) {
if(extension.equalsIgnoreCase(allowedExtension)){
return true;
}
}
return false;
}
/**
* 此處自定義檔名,uuid + extension
*/
private String getPathName(MultipartFile file) {
String extension = getExtension(file);
return UUID.randomUUID().toString() + "." + extension;
}
/**
* 獲取副檔名
*/
private String getExtension(MultipartFile file) {
String originalFilename = file.getOriginalFilename();
return originalFilename.substring(originalFilename.lastIndexOf('.') + 1);
}
}
五、原始碼下載
本文內容均為對優秀部落格及官方文件總結而得,原文地址均已在文中參考閱讀處標註。最後,文中的程式碼樣例已經全部上傳至Gitee:https://gitee.com/tqbx/springboot-samples-learn,另有其他SpringBoot的整合哦。