本示例記錄Spring Boot 2.x中文件的上傳,整個(gè)上傳邏輯只有兩三行代碼。
一、文件的上傳
@RequestMapping("files")
@RestController
public class FileController {
// 確定存儲(chǔ)文件的目錄
private String path = "d:" + File.separator + "uploader";
@PostMapping
public Map<String, Object> upload(@RequestParam("file") MultipartFile file)
throws IllegalStateException, IOException {
// 獲取原始文件名
String filename = file.getOriginalFilename();
// 構(gòu)建保存目標(biāo)
File target = new File(path + File.separator + filename);
// 將文件轉(zhuǎn)移到指定目錄
file.transferTo(target);
// 構(gòu)建響應(yīng)
Map<String, Object> response = new HashMap<>();
response.put("target", target.getAbsolutePath());
return response;
}
@PostConstruct
public void initStorage() {
File target = new File(path);
if (!target.exists()) {
if (target.mkdirs()) {
System.out.println("文件存儲(chǔ)目錄創(chuàng)建成功。");
} else {
System.err.println("文件存儲(chǔ)目錄創(chuàng)建失敗。");
}
}
}
}
代碼解析:
基于Restful風(fēng)格構(gòu)建的文件上傳邏輯。文件上傳的邏輯寫在upload方法中。
- 使用屬性path存儲(chǔ)文件的保存位置,這個(gè)可以放在配置文件中。
- MultipartFile是Spring提供的,解析multipart request后,對(duì)于請(qǐng)求中的文件的包裝。它提供了一些常用的方法來操作文件。包括獲取文件名,獲取文件長(zhǎng)度,獲取原始文件名等。
- 使用MultipartFile.transferTo(target)將文件保存到指定目標(biāo)位置。
- initStorage方法用于在系統(tǒng)啟動(dòng)時(shí),初始化文件存儲(chǔ)目錄,這里沒有考慮到各種異常情況。上傳的關(guān)鍵不在這里。
- 關(guān)于異常處理:在上傳的邏輯中沒有處理各種異常,轉(zhuǎn)而交由Spring boot的統(tǒng)一異常處理邏輯來處理異常。
測(cè)試:
使用postman進(jìn)行一個(gè)簡(jiǎn)單的測(cè)試
測(cè)試一.png - 注意輸入正確的地址
- 方法選擇post
- body選擇form-data
- 給定一個(gè)名稱為file參數(shù)(和controller中的RequestParam對(duì)應(yīng)),value選擇一個(gè)文件。
可能發(fā)生的錯(cuò)誤 - Maximum upload size exceeded。這時(shí)由于上傳文件的大小超過了服務(wù)器可以接受的上限導(dǎo)致的。
可以通過配置服務(wù)器請(qǐng)求大小限制來解決,修改application.yml,添加如下配置
spring:
servlet:
multipart:
max-file-size: 200MB
max-request-size: 200MB
- spring.servlet.multipart.max-file-size #用于控制multipart request中,單個(gè)文件的大小
- spring.servlet.multipart.max-request-size #用于控制multipart request中,整體請(qǐng)求的大小(一個(gè)上傳請(qǐng)求可以添加多個(gè)文件,所有文件的大小總和不能超過該數(shù)值)。
二、文件的下載
繼續(xù)在controller中添加下載代碼邏輯
@GetMapping("{filename}")
public ResponseEntity<InputStreamSource> download(@PathVariable("filename") String filename) {
// 構(gòu)建下載路徑
File target = new File(path + File.separator + filename);
// 構(gòu)建響應(yīng)體
if (target.exists()) {
FileSystemResource resource = new FileSystemResource(target);
return ResponseEntity.ok()
// 指定文件的contentType
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(resource);
} else {
// 如果文件不存在,返回404響應(yīng)
return ResponseEntity.notFound().build();
}
}
代碼解析:
- 使用restful風(fēng)格的api
- 使用ResponseEntity<InputStreamSource>作為響應(yīng)。
- InputStreamSource是Spring提供的,它能簡(jiǎn)化對(duì)于使用InputStream作為響應(yīng)時(shí)的編程。網(wǎng)上很多其它文章都是通過獲取response中的outputStream,然后向outputStream中寫入數(shù)據(jù)來實(shí)現(xiàn)下載,稍顯復(fù)雜。
- FileSystemResource是InputStreamSource的一個(gè)實(shí)現(xiàn)。它使用nio來實(shí)現(xiàn)數(shù)據(jù)的復(fù)制,能夠顯著的減少在文件下載時(shí)CPU的使用量(具體實(shí)現(xiàn)在不同的平臺(tái)中會(huì)有所差別,相關(guān)概念參考 《零復(fù)制》)。
小結(jié)
這應(yīng)該是Spring boot中最簡(jiǎn)單的文件上傳和下載功能了,而且從理論上來將,性能也是比較優(yōu)秀的(利用了nio)。但功能還是相對(duì)簡(jiǎn)單。接下來還會(huì)實(shí)現(xiàn)文件的斷點(diǎn)續(xù)傳,視頻的拖動(dòng)播放等功能。
示例參考 https://github.com/ldwqh0/file-uploader.git
相關(guān)文章
spring boot 三兩行代碼實(shí)現(xiàn)文件的上傳和下載
spring boot 文件下載的預(yù)覽和緩存
Spring配合Nginx實(shí)現(xiàn)文件下載
