SpringBoot實現檔案上傳功能詳解
阿新 • • 發佈:2020-11-13
[toc]
# 利用SpirngBoot實現檔案上傳功能
## 零、本篇要點
- 介紹SpringBoot對檔案上傳的自動配置。
- 介紹MultipartFile介面。
- 介紹SpringBoot+Thymeleaf檔案上傳demo的整合。
- 介紹對檔案型別,檔名長度等判斷方法。
## 一、SpringBoot對檔案處理相關自動配置
自動配置是SpringBoot為我們提供的便利之一,開發者可以在不作任何配置的情況下,使用SpringBoot提供的預設設定,如處理檔案需要的MultipartResolver。
```java
@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強大介面,讓我們能夠獲取上傳檔案的詳細資訊,如原始檔名,內容型別等等,介面內容如下:
```java
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、編寫控制器
```java
/**
* 檔案上傳
*
* @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
檔案上傳介面
```
### 3、編寫頁面uploadStatus.html
```html
檔案上傳介面
```
### 4、編寫配置
```properties
server.port=8081
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
```
### 5、配置虛擬路徑對映
這一步是非常重要的,我們將檔案上傳到伺服器上時,我們需要將我們的請求路徑和伺服器上的路徑進行對應,不然很有可能檔案上傳成功,但訪問失敗:
```java
@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,並且頁面也成功顯示:
![](https://img2020.cnblogs.com/blog/1771072/202011/1771072-20201113151153063-531316526.png)
## 四、SpringBoot的Restful風格,返回url
```java
/**
* 檔案上傳
*
* @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 singleFileUpload(@RequestParam("file") MultipartFile file) throws Exception {
if (file.isEmpty()) {
throw new Exception("檔案為空!");
}
String filename = upload(file);
String url = "/upload/" + filename;
Map 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](https://gitee.com/tqbx/springboot-samples-learn),另有其他SpringBoot的整合哦。
## 六、參考閱讀
- [官方文件:SpringWebMVC#DispatcherServlet#Multipart Resolver](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-multipart)
- [Spring Boot file upload example](https://mkyong.com/spring-boot/spring-boot-file-upload-example/)