Spring Boot 2.0實現基於Restful風格的文件上傳與下載APIs

文件上傳與下載在Web應用中是一個比較常見的功能。在本教程中,我將基於Spring 2.2.6版本實現一個基於Restful風格的文件上傳與下載APIs。
基於Spring Boot 2.0實戰系列源碼已經Push到Github倉庫: https://github.com/ramostear/... 。感興趣朋友歡迎Star/Fork。

1. 環境

  • JDK: Java 1.8
  • Framework: Spring Boot 2.2.6(Only Using Spring Web MVC)
  • Maven: Maven 3.5.0+
  • IDE: IntelliJ IDEA 2019.2
  • Test: Postman 7.23.0

2. 功能

本教程中,使用Spring 2.2.6實現Restful風格的APIs並提供如下的功能:java

  • 1.客戶端上傳文件到服務端
  • 2.對客戶端上傳文件大小進行限制(50MB)
  • 3.點擊連接地址下載文件
  • 4.得到已上傳文件列表(文件名和下載地址)

下面是教程所實現的APIs列表(服務端請求端口默認8080):git

請求方式 URL地址 說明
POST /upload 上傳一份文件
GET /files 獲取已上傳文件列表
GET /files/{filename} 根據連接地址下載文件

3.工程結構

工程目錄結構說明以下:github

  • 1.config/FileUploadConfiguration.java: 常規組件,主要在重啓應用時清理歷史文件;
  • 2.controller/FileUploadController.java: 主要的控制器,負責處理文件的上傳,下載,瀏覽等請求;
  • 3.exception/FileUploadExceptionAdvice.java: 全局的異常處理類,提供用戶友好的異常提示信息;
  • 4.service/FileStorageService.java: 文件上傳接口類,提供存儲地址初始化,保存文件,加載文件,清理文件等操做;
  • 5.service/impl/FileStorageServiceImpl.java: 文件上傳接口實現類;
  • 6.valueobject/UploadFile.java: 封裝了文件名和存儲地址的POJO類;
  • 7.valueobject/Message.java: 請求/響應的消息對象;
  • 8.resources/application.yml: 項目配置文件,主要配置了文件上傳大小限制;
  • 9.pom.xml:Maven依賴配置文件。

4 建立Spring Boot項目

本教程是基於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依賴。

4.1 文件上傳接口

按照面向接口編程的約定(規範),建立一個用於操做上傳文件的接口類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()方法初始化文件上傳地址。

4.2 實現文件上傳接口

文件上傳接口實現類比較簡單,這裏直接給出代碼:瀏覽器

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 -&gt; !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包中提供的類。

4.3 定義值對象

本教程中,定義了兩個簡單的對象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;
    }
}

4.4 控制器

在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>&gt; files(){
        List<uploadfile> files = fileStorageService.load()
                .map(path -&gt; {
                    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的快捷方式註解方法。

4.5配置上傳文件大小

一般,出於安全和性能考慮,咱們須要限定客戶端上傳文件的大小,本教程限定的文件大小最大爲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:單個文件所能上傳的文件大小

4.6 全局異常處理

在控制器中,文件上傳過程當中可能產生的異常咱們使用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."));
    }
}

4.7 初始化文件存儲空間

爲了在測試時得到乾淨的測試數據,同時也爲了在應用啓動後分配好上傳文件存儲地址,咱們須要在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中。

5.運行程序並測試

運行Spring Boot應用程序的方式有不少,例如:

  • 1.命令方式:mvn spring-boot:run
  • 2.IntelliJ IDEA:點擊IntelliJ IDEA的「Run」按鈕
  • 3.main()方法:直接運行主類中的main()方法
  • 4.運行jar包:java -jar springboot-fileupload.jar

選擇一種你比較熟悉的方式運行Spring Boot應用程序。當應用程序啓動成功後,在項目的根目錄會建立一個名爲fileStorage的文件夾,該文件夾將用於存放客戶端上傳的文件。

5.1 使用Postman對APIs進行測試

應用程序啓動成功後,咱們使用Postman對應用程序中的APIs進行測試。

$調用/upload接口上傳文件:

$上傳一個大小超過50MB的文件

執行結果:

$檢查文件存儲文件夾

文件上傳成功後,咱們能夠查看項目根目錄下的fileStorage文件夾,檢查是否有文件被存儲到當中:

$調用/files接口,獲取全部已上傳文件列表

/files接口將返回全部已上傳的文件信息,咱們能夠點擊其中任意一個連接地址下載文件。在Postman中,能夠經過header選項卡查看響應頭中文件的詳細信息,例如:

你也能夠複製列表中的連接地址,並在瀏覽器中訪問該地址,瀏覽器會彈出一個下載詢問對話框,點擊肯定按鈕進行下載。

6 總結

本章節介紹了Spring Boot 2.0實現基於Restful風格的文件上傳和下載APIs,並使用Postman工具對APIs進行測試,達到了設計的預期結果。你能夠經過下面的連接地址獲取本次教程的相關源代碼。

Github倉庫地址
https://github.com/ramostear/...

若是你在運行本次教程提供的源代碼過程當中遇到什麼問題,請在評論區與我聯繫。


未經容許,請勿轉載!
相關文章
相關標籤/搜索