• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

SpringBoot 2.0实现Restful风格的文件上传和下载

武飞扬头像
程序员小明1024
帮助1

学新通

文件上传与下载在Web应用中是一个比较常见的功能。在本教程中,我将基于Spring 2.2.6版本实现一个基于Restful风格的文件上传与下载APIs。

基于Spring Boot 2.0实战系列源码已经Push到Github仓库:github.com/ramostear/s… 。感兴趣朋友欢迎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并提供以下的功能:

  • 1.客户端上传文件到服务端
  • 2.对客户端上传文件大小进行限制(50MB)
  • 3.点击链接地址下载文件
  • 4.获得已上传文件列表(文件名和下载地址)

下面是教程所实现的APIs列表(服务端请求端口默认8080):

请求方式 URL地址 说明
POST /upload 上传一份文件
GET /files 获取已上传文件列表
GET /files/{filename} 根据链接地址下载文件

3.工程结构

学新通

工程目录结构说明如下:

  • 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文件中是否包含如下配置:

  1.  
    <dependency>
  2.  
    <groupId>org.springframework.boot</groupId>
  3.  
    <artifactId>spring-boot-starter-web</artifactId>
  4.  
    </dependency>
  5.  
    复制代码

本教程只使用到Spring Web MVC的功能,因此只需添加spring-boot-starter-web依赖。

4.1 文件上传接口

按照面向接口编程的约定(规范),创建一个用于操作上传文件的接口类FileStorageService.java,并提供相应的方法。

service/FileStorageService.java

  1.  
    package com.ramostear.springboot.uploadfile.service;
  2.  
     
  3.  
    import org.springframework.core.io.Resource;
  4.  
    import org.springframework.web.multipart.MultipartFile;
  5.  
     
  6.  
    import java.nio.file.Path;
  7.  
    import java.util.stream.Stream;
  8.  
     
  9.  
    /**
  10.  
    * @ClassName FileStorageService
  11.  
    * @Description TODO
  12.  
    * @Author 树下魅狐
  13.  
    * @Date 2020/4/28 0028 18:35
  14.  
    * @Version since 1.0
  15.  
    **/
  16.  
    public interface FileStorageService {
  17.  
     
  18.  
    void init();
  19.  
     
  20.  
    void save(MultipartFile multipartFile);
  21.  
     
  22.  
    Resource load(String filename);
  23.  
     
  24.  
    Stream<Path> load();
  25.  
     
  26.  
    void clear();
  27.  
     
  28.  
    }
  29.  
    复制代码
学新通

在启动应用时,先调用clear()方法清理历史文件,再调用init()方法初始化文件上传地址。

4.2 实现文件上传接口

文件上传接口实现类比较简单,这里直接给出代码:

service/impl/FileStorageServiceImpl.java

  1.  
    /**
  2.  
    * @ClassName FileStorageServiceImpl
  3.  
    * @Description TODO
  4.  
    * @Author 树下魅狐
  5.  
    * @Date 2020/4/28 0028 18:38
  6.  
    * @Version since 1.0
  7.  
    **/
  8.  
    @Service("fileStorageService")
  9.  
    public class FileStorageServiceImpl implements FileStorageService {
  10.  
     
  11.  
    private final Path path = Paths.get("fileStorage");
  12.  
     
  13.  
     
  14.  
    @Override
  15.  
    public void init() {
  16.  
    try {
  17.  
    Files.createDirectory(path);
  18.  
    } catch (IOException e) {
  19.  
    throw new RuntimeException("Could not initialize folder for upload!");
  20.  
    }
  21.  
    }
  22.  
     
  23.  
    @Override
  24.  
    public void save(MultipartFile multipartFile) {
  25.  
    try {
  26.  
    Files.copy(multipartFile.getInputStream(),this.path.resolve(multipartFile.getOriginalFilename()));
  27.  
    } catch (IOException e) {
  28.  
    throw new RuntimeException("Could not store the file. Error:" e.getMessage());
  29.  
    }
  30.  
    }
  31.  
     
  32.  
    @Override
  33.  
    public Resource load(String filename) {
  34.  
    Path file = path.resolve(filename);
  35.  
    try {
  36.  
    Resource resource = new UrlResource(file.toUri());
  37.  
    if(resource.exists() || resource.isReadable()){
  38.  
    return resource;
  39.  
    }else{
  40.  
    throw new RuntimeException("Could not read the file.");
  41.  
    }
  42.  
    } catch (MalformedURLException e) {
  43.  
    throw new RuntimeException("Error:" e.getMessage());
  44.  
    }
  45.  
    }
  46.  
     
  47.  
    @Override
  48.  
    public Stream<Path> load() {
  49.  
    try {
  50.  
    return Files.walk(this.path,1)
  51.  
    .filter(path -> !path.equals(this.path))
  52.  
    .map(this.path::relativize);
  53.  
    } catch (IOException e) {
  54.  
    throw new RuntimeException("Could not load the files.");
  55.  
    }
  56.  
    }
  57.  
     
  58.  
    @Override
  59.  
    public void clear() {
  60.  
    FileSystemUtils.deleteRecursively(path.toFile());
  61.  
    }
  62.  
    }
  63.  
    复制代码
学新通

其中,Files、Path和Paths是java.nio.file提供的类,Resource是org.springframework.core.io包中提供的类。

4.3 定义值对象

本教程中,定义了两个简单的对象UploadFile.java和Message.java,分别封装了上传文件信息和响应消息,代码如下:

valueobject/UploadFile.java

  1.  
    /**
  2.  
    * @ClassName UploadFile
  3.  
    * @Description TODO
  4.  
    * @Author 树下魅狐
  5.  
    * @Date 2020/4/28 0028 18:48
  6.  
    * @Version since 1.0
  7.  
    **/
  8.  
    public class UploadFile {
  9.  
     
  10.  
    private String fileName;
  11.  
    private String url;
  12.  
     
  13.  
    public UploadFile(String fileName, String url) {
  14.  
    this.fileName = fileName;
  15.  
    this.url = url;
  16.  
    }
  17.  
     
  18.  
    public String getFileName() {
  19.  
    return fileName;
  20.  
    }
  21.  
     
  22.  
    public void setFileName(String fileName) {
  23.  
    this.fileName = fileName;
  24.  
    }
  25.  
     
  26.  
    public String getUrl() {
  27.  
    return url;
  28.  
    }
  29.  
     
  30.  
    public void setUrl(String url) {
  31.  
    this.url = url;
  32.  
    }
  33.  
    }
  34.  
    复制代码
学新通

valueobject/Message.java

  1.  
    /**
  2.  
    * @ClassName Message
  3.  
    * @Description TODO
  4.  
    * @Author 树下魅狐
  5.  
    * @Date 2020/4/28 0028 19:21
  6.  
    * @Version since 1.0
  7.  
    **/
  8.  
    public class Message {
  9.  
     
  10.  
    private String message;
  11.  
     
  12.  
    public Message(String message) {
  13.  
    this.message = message;
  14.  
    }
  15.  
     
  16.  
    public String getMessage() {
  17.  
    return message;
  18.  
    }
  19.  
     
  20.  
    public void setMessage(String message) {
  21.  
    this.message = message;
  22.  
    }
  23.  
    }
  24.  
    复制代码
学新通

4.4 控制器

在controller包下创建文件上传控制器,用于处理客户端的请求。代码如下:

controller/FileUploadController.java

  1.  
    /**
  2.  
    * @ClassName FileUploadController
  3.  
    * @Description TODO
  4.  
    * @Author 树下魅狐
  5.  
    * @Date 2020/4/28 0028 18:52
  6.  
    * @Version since 1.0
  7.  
    **/
  8.  
    @RestController
  9.  
    public class FileUploadController {
  10.  
     
  11.  
    @Autowired
  12.  
    FileStorageService fileStorageService;
  13.  
     
  14.  
    @PostMapping("/upload")
  15.  
    public ResponseEntity<Message> upload(@RequestParam("file")MultipartFile file){
  16.  
    try {
  17.  
    fileStorageService.save(file);
  18.  
    return ResponseEntity.ok(new Message("Upload file successfully: " file.getOriginalFilename()));
  19.  
    }catch (Exception e){
  20.  
    return ResponseEntity.badRequest()
  21.  
    .body(new Message("Could not upload the file:" file.getOriginalFilename()));
  22.  
    }
  23.  
    }
  24.  
     
  25.  
    @GetMapping("/files")
  26.  
    public ResponseEntity<List<UploadFile>> files(){
  27.  
    List<UploadFile> files = fileStorageService.load()
  28.  
    .map(path -> {
  29.  
    String fileName = path.getFileName().toString();
  30.  
    String url = MvcUriComponentsBuilder
  31.  
    .fromMethodName(FileUploadController.class,
  32.  
    "getFile",
  33.  
    path.getFileName().toString()
  34.  
    ).build().toString();
  35.  
    return new UploadFile(fileName,url);
  36.  
    }).collect(Collectors.toList());
  37.  
    return ResponseEntity.ok(files);
  38.  
    }
  39.  
     
  40.  
    @GetMapping("/files/{filename:. }")
  41.  
    public ResponseEntity<Resource> getFile(@PathVariable("filename")String filename){
  42.  
    Resource file = fileStorageService.load(filename);
  43.  
    return ResponseEntity.ok()
  44.  
    .header(HttpHeaders.CONTENT_DISPOSITION,
  45.  
    "attachment;filename=\"" file.getFilename() "\"")
  46.  
    .body(file);
  47.  
    }
  48.  
    }
  49.  
    复制代码
学新通

在控制器中,使用@RestController组合注解替换了@Controller @ResponseBody的注解方式,并采用@RequestMapping的快捷方式注解方法。

4.5配置上传文件大小

通常,出于安全和性能考虑,我们需要限定客户端上传文件的大小,本教程限定的文件大小最大为50MB。在application.yml(application.properties)文件中添加如下配置:

application.yml

  1.  
    spring:
  2.  
    servlet:
  3.  
    multipart:
  4.  
    max-request-size: 50MB
  5.  
    max-file-size: 50MB
  6.  
     

application.properties

  1.  
    spring.servlet.multipart.max-request-size=50MB
  2.  
    spring.servlet.multipart.max-file-size=50MB
  3.  
     
  • 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

  1.  
    /**
  2.  
    * @ClassName FileUploadExceptionAdvice
  3.  
    * @Description TODO
  4.  
    * @Author 树下魅狐
  5.  
    * @Date 2020/4/28 0028 19:10
  6.  
    * @Version since 1.0
  7.  
    **/
  8.  
    @ControllerAdvice
  9.  
    public class FileUploadExceptionAdvice extends ResponseEntityExceptionHandler {
  10.  
     
  11.  
    @ExceptionHandler(MaxUploadSizeExceededException.class)
  12.  
    public ResponseEntity<Message> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e){
  13.  
    return ResponseEntity.badRequest().body(new Message("Upload file too large."));
  14.  
    }
  15.  
    }
  16.  
    复制代码
学新通

4.7 初始化文件存储空间

为了在测试时获得干净的测试数据,同时也为了在应用启动后分配好上传文件存储地址,我们需要在config包下创建一个配置类,在应用启动时调用FileStorageService中的clear()方法和init()方法。实现该功能,最快的方式是配置类实现CommandLineRunner接口类的run()方法,代码如下:

config/FileUploadConfiguration.java

  1.  
    @Service
  2.  
    public class FileUploadConfiguration implements CommandLineRunner {
  3.  
     
  4.  
    @Autowired
  5.  
    FileStorageService fileStorageService;
  6.  
     
  7.  
    @Override
  8.  
    public void run(String... args) throws Exception {
  9.  
    fileStorageService.clear();
  10.  
    fileStorageService.init();
  11.  
    }
  12.  
    }
  13.  
    复制代码

使用@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仓库地址

github.com/ramostear/s…

如果你在运行本次教程提供的源代码过程中遇到什么问题,请在评论区与我联系。


未经允许,请勿转载!

来源:https://juejin.cn/post/6844904165794152456

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhhaefab
系列文章
更多 icon
同类精品
更多 icon
继续加载