文件上傳與下載在Web應用中是一個比較常見的功能。在本教程中,我將基於Spring 2.2.6版本實現一個基於Restful風格的文件上傳與下載APIs。
基於Spring Boot 2.0實戰系列源碼已經Push到Github倉庫: https://github.com/ramostear/... 。感興趣朋友歡迎Star/Fork。
本教程中,使用Spring 2.2.6實現Restful風格的APIs並提供如下的功能:java
下面是教程所實現的APIs列表(服務端請求端口默認8080):git
請求方式 | URL地址 | 說明 |
---|---|---|
POST | /upload | 上傳一份文件 |
GET | /files | 獲取已上傳文件列表 |
GET | /files/{filename} | 根據連接地址下載文件 |
工程目錄結構說明以下:github
本教程是基於IntelliJ IDEA建立Spring Boot項目的,你也能夠選擇本身喜歡的IDE建立項目。建立完項目後,請檢查pom.xml文件中是否包含以下配置:web
<dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> </dependency>
本教程只使用到Spring Web MVC的功能,所以只需添加spring-boot-starter-web依賴。
按照面向接口編程的約定(規範),建立一個用於操做上傳文件的接口類FileStorageService.java,並提供相應的方法。spring
service/FileStorageService.java編程
package com.ramostear.springboot.uploadfile.service; import org.springframework.core.io.Resource; import org.springframework.web.multipart.MultipartFile; import java.nio.file.Path; import java.util.stream.Stream; /** * @ClassName FileStorageService * @Description TODO * @Author 樹下魅狐 * @Date 2020/4/28 0028 18:35 * @Version since 1.0 **/ public interface FileStorageService { void init(); void save(MultipartFile multipartFile); Resource load(String filename); Stream<path> load(); void clear(); }
在啓動應用時,先調用clear()方法清理歷史文件,再調用init()方法初始化文件上傳地址。
文件上傳接口實現類比較簡單,這裏直接給出代碼:瀏覽器
service/impl/FileStorageServiceImpl.java安全
/** * @ClassName FileStorageServiceImpl * @Description TODO * @Author 樹下魅狐 * @Date 2020/4/28 0028 18:38 * @Version since 1.0 **/ @Service("fileStorageService") public class FileStorageServiceImpl implements FileStorageService { private final Path path = Paths.get("fileStorage"); @Override public void init() { try { Files.createDirectory(path); } catch (IOException e) { throw new RuntimeException("Could not initialize folder for upload!"); } } @Override public void save(MultipartFile multipartFile) { try { Files.copy(multipartFile.getInputStream(),this.path.resolve(multipartFile.getOriginalFilename())); } catch (IOException e) { throw new RuntimeException("Could not store the file. Error:"+e.getMessage()); } } @Override public Resource load(String filename) { Path file = path.resolve(filename); try { Resource resource = new UrlResource(file.toUri()); if(resource.exists() || resource.isReadable()){ return resource; }else{ throw new RuntimeException("Could not read the file."); } } catch (MalformedURLException e) { throw new RuntimeException("Error:"+e.getMessage()); } } @Override public Stream<path> load() { try { return Files.walk(this.path,1) .filter(path -> !path.equals(this.path)) .map(this.path::relativize); } catch (IOException e) { throw new RuntimeException("Could not load the files."); } } @Override public void clear() { FileSystemUtils.deleteRecursively(path.toFile()); } }
其中,Files、Path和Paths是java.nio.file提供的類,Resource是org.springframework.core.io包中提供的類。
本教程中,定義了兩個簡單的對象UploadFile.java和Message.java,分別封裝了上傳文件信息和響應消息,代碼以下:springboot
valueobject/UploadFile.javaapp
/** * @ClassName UploadFile * @Description TODO * @Author 樹下魅狐 * @Date 2020/4/28 0028 18:48 * @Version since 1.0 **/ public class UploadFile { private String fileName; private String url; public UploadFile(String fileName, String url) { this.fileName = fileName; this.url = url; } public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = fileName; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } }
valueobject/Message.java
/** * @ClassName Message * @Description TODO * @Author 樹下魅狐 * @Date 2020/4/28 0028 19:21 * @Version since 1.0 **/ public class Message { private String message; public Message(String message) { this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
在controller包下建立文件上傳控制器,用於處理客戶端的請求。代碼以下:
controller/FileUploadController.java
/** * @ClassName FileUploadController * @Description TODO * @Author 樹下魅狐 * @Date 2020/4/28 0028 18:52 * @Version since 1.0 **/ @RestController public class FileUploadController { @Autowired FileStorageService fileStorageService; @PostMapping("/upload") public ResponseEntity<message> upload(@RequestParam("file")MultipartFile file){ try { fileStorageService.save(file); return ResponseEntity.ok(new Message("Upload file successfully: "+file.getOriginalFilename())); }catch (Exception e){ return ResponseEntity.badRequest() .body(new Message("Could not upload the file:"+file.getOriginalFilename())); } } @GetMapping("/files") public ResponseEntity<list<uploadfile>> files(){ List<uploadfile> files = fileStorageService.load() .map(path -> { String fileName = path.getFileName().toString(); String url = MvcUriComponentsBuilder .fromMethodName(FileUploadController.class, "getFile", path.getFileName().toString() ).build().toString(); return new UploadFile(fileName,url); }).collect(Collectors.toList()); return ResponseEntity.ok(files); } @GetMapping("/files/{filename:.+}") public ResponseEntity<resource> getFile(@PathVariable("filename")String filename){ Resource file = fileStorageService.load(filename); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=\""+file.getFilename()+"\"") .body(file); } }
> 在控制器中,使用@RestController組合註解替換了@Controller+@ResponseBody的註解方式,並採用@RequestMapping的快捷方式註解方法。
一般,出於安全和性能考慮,咱們須要限定客戶端上傳文件的大小,本教程限定的文件大小最大爲50MB。在application.yml(application.properties)文件中添加以下配置:
application.yml
spring: servlet: multipart: max-request-size: 50MB max-file-size: 50MB
application.properties
spring.servlet.multipart.max-request-size=50MB spring.servlet.multipart.max-file-size=50MB
- spring.servlet.multipart.max-request-size=50MB: 單次請求所能上傳文件的總文件大小
- spring.servlet.multipart.max-file-size=50MB:單個文件所能上傳的文件大小
在控制器中,文件上傳過程當中可能產生的異常咱們使用try-catch語句進行了用戶友好處理,但當客戶端上傳文件大小超過50MB時,應用會拋出MaxUploadSizeExceededException異常信息,咱們須要對此異常信息作處理。最簡單的方式是使用@ControllerAdvice+@ExceptionHandler組合方式處理異常。在exception包下建立異常處理類,代碼以下:
exception/FileUploadExceptionAdvice.java
/** * @ClassName FileUploadExceptionAdvice * @Description TODO * @Author 樹下魅狐 * @Date 2020/4/28 0028 19:10 * @Version since 1.0 **/ @ControllerAdvice public class FileUploadExceptionAdvice extends ResponseEntityExceptionHandler { @ExceptionHandler(MaxUploadSizeExceededException.class) public ResponseEntity<message> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e){ return ResponseEntity.badRequest().body(new Message("Upload file too large.")); } }
爲了在測試時得到乾淨的測試數據,同時也爲了在應用啓動後分配好上傳文件存儲地址,咱們須要在config包下建立一個配置類,在應用啓動時調用FileStorageService中的clear()方法和init()方法。實現該功能,最快的方式是配置類實現CommandLineRunner接口類的run()方法,代碼以下:
config/FileUploadConfiguration.java
@Service public class FileUploadConfiguration implements CommandLineRunner { @Autowired FileStorageService fileStorageService; @Override public void run(String... args) throws Exception { fileStorageService.clear(); fileStorageService.init(); } }
使用@Autowired註解將FileStorageService注入到FileUploadConfiguration.java中。
運行Spring Boot應用程序的方式有不少,例如:
選擇一種你比較熟悉的方式運行Spring Boot應用程序。當應用程序啓動成功後,在項目的根目錄會建立一個名爲fileStorage的文件夾,該文件夾將用於存放客戶端上傳的文件。
應用程序啓動成功後,咱們使用Postman對應用程序中的APIs進行測試。
執行結果:
文件上傳成功後,咱們能夠查看項目根目錄下的fileStorage文件夾,檢查是否有文件被存儲到當中:
/files接口將返回全部已上傳的文件信息,咱們能夠點擊其中任意一個連接地址下載文件。在Postman中,能夠經過header選項卡查看響應頭中文件的詳細信息,例如:
你也能夠複製列表中的連接地址,並在瀏覽器中訪問該地址,瀏覽器會彈出一個下載詢問對話框,點擊肯定按鈕進行下載。
本章節介紹了Spring Boot 2.0實現基於Restful風格的文件上傳和下載APIs,並使用Postman工具對APIs進行測試,達到了設計的預期結果。你能夠經過下面的連接地址獲取本次教程的相關源代碼。
Github倉庫地址
https://github.com/ramostear/...
若是你在運行本次教程提供的源代碼過程當中遇到什麼問題,請在評論區與我聯繫。
未經容許,請勿轉載!