前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringBoot 实现基于Restful风格的文件上传与下载

SpringBoot 实现基于Restful风格的文件上传与下载

作者头像
会呼吸的Coder
发布2021-09-02 10:27:34
3.6K0
发布2021-09-02 10:27:34
举报
文章被收录于专栏:会呼吸的Coder会呼吸的Coder

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

Part1环境

  • 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

Part2功能

本教程中,使用Spring 2.2.6实现Restful风格的APIs并提供以下的功能:

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

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

Part3工程结构

工程目录结构说明如下:

  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依赖配置文件。

Part4创建Spring Boot项目

本教程是基于IntelliJ IDEA创建Spring Boot项目的,你也可以选择自己喜欢的IDE创建项目。创建完项目后,请检查pom.xml文件中是否包含如下配置:

代码语言:javascript
复制
<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-web</artifactId>  
</dependency>  

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

4.1 文件上传接口

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

service/FileStorageService.java

代码语言:javascript
复制
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

代码语言:javascript
复制
/**  
 * @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包中提供的类。

4.3 定义值对象

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

valueobject/UploadFile.java

代码语言:javascript
复制
/**  
 * @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;  
    }  
}  
代码语言:javascript
复制

valueobject/Message.java

代码语言:javascript
复制
/**  
 * @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;  
    }  
}
代码语言:javascript
复制

4.4 控制器

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

controller/FileUploadController.java

代码语言:javascript
复制
/**  
 * @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的快捷方式注解方法。

4.5配置上传文件大小

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

application.yml

代码语言:javascript
复制
spring:  
  servlet:  
    multipart:  
      max-request-size: 50MB  
      max-file-size: 50MB
代码语言:javascript
复制

application.properties

代码语言:javascript
复制
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

代码语言:javascript
复制
/**  
 * @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."));  
    }  
}
代码语言:javascript
复制
4.7 初始化文件存储空间

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

config/FileUploadConfiguration.java

代码语言:javascript
复制
@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中。

Part5运行程序并测试

运行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选项卡查看响应头中文件的详细信息,例如:

你也可以复制列表中的链接地址,并在浏览器中访问该地址,浏览器会弹出一个下载询问对话框,点击确定按钮进行下载。

Part6 总结

本章节介绍了Spring Boot 2.0实现基于Restful风格的文件上传和下载APIs,并使用Postman工具对APIs进行测试,达到了设计的预期结果。你可以通过下面的链接地址获取本次教程的相关源代码。

Github仓库地址 https://github.com/ramostear/springboot2.0-action

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-08-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 初级程序员 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Part1环境
  • Part2功能
  • Part3工程结构
  • Part4创建Spring Boot项目
    • 4.1 文件上传接口
      • 4.2 实现文件上传接口
        • 4.3 定义值对象
          • 4.4 控制器
            • 4.5配置上传文件大小
              • 4.6 全局异常处理
              • Part5运行程序并测试
                • 5.1 使用Postman对APIs进行测试
                • Part6 总结
                相关产品与服务
                文件存储
                文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档