Yuandupier

Yuandupier

Spring Boot中文件上传

59
0
0
2023-03-21

前言

本篇主要参考Spring官方文档,整理了Spring Boot中文件上传如何实现,以及在代码中使用RestTemplate和HttpClient两种方式实现文件上传。

创建Spring Boot项目

首先创建一个Spring Boot Web项目,使用的Spring Boot版本为2.6.14,项目的pom文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.yzh</groupId>
    <artifactId>uploadFile</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.14</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpmime</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

主要功能

提供下面三个功能:

方法URL功能
POST/upload上传文件
GET/files获取文件列表
GET/files/{fileName}下载文件

具体实现

代码结构

在这里插入图片描述

主要功能实现

这边直接贴一下代码:

controller

/**
 * 文件上传
 *
 * @author yuanzhihao
 * @since 2023/3/22
 */
@RestController
@RequestMapping
public class FileUploadController {

    @Autowired
    private FileUploadService fileUploadService;


    /**
     * 上传文件
     *
     * @param files 文件
     * @return 响应消息
     */
    @PostMapping("/upload")
    public ResponseEntity<String> upload(@RequestParam("files") MultipartFile[] files) {
        fileUploadService.upload(files);
        return ResponseEntity.ok("File Upload Success");
    }

    /**
     * 获取文件列表
     *
     * @return 文件列表
     */
    @GetMapping("/files")
    public ResponseEntity<List<FileInfo>> list() {
        return ResponseEntity.ok(fileUploadService.list());
    }

    /**
     * 获取指定文件
     *
     * @param fileName 文件名称
     * @return 文件
     */
    @GetMapping("/files/{fileName:.+}")
    public ResponseEntity<Resource> getFile(@PathVariable("fileName") String fileName) {
        return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION,
                "attachment; filename=\"" + fileName + "\"").body(fileUploadService.getFile(fileName));
    }
}

service

/**
 * 文件上传Service
 *
 * @author yuanzhihao
 * @since 2023/3/27
 */
public interface FileUploadService {

    void upload(MultipartFile[] files);

    List<FileInfo> list();

    Resource getFile(String fileName);
}
/**
 * 文件上传
 *
 * @author yuanzhihao
 * @since 2023/3/27
 */
@Service
@Slf4j
public class FileUploadServiceImpl implements FileUploadService {
    @Value("${upload.path:/data/upload/}")
    private String filePath;

    private static final List<FileInfo> FILE_STORAGE = new CopyOnWriteArrayList<>();

    @Override
    public void upload(MultipartFile[] files) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for (MultipartFile file : files) {
            String fileName = file.getOriginalFilename();
            boolean match = FILE_STORAGE.stream().anyMatch(fileInfo -> fileInfo.getFileName().equals(fileName));
            if (match) {
                throw new RuntimeException("File [ " + fileName + " ] already exist");
            }

            String currentTime = simpleDateFormat.format(new Date());
            try (InputStream in = file.getInputStream();
                 OutputStream out = Files.newOutputStream(Paths.get(filePath + fileName))) {
                FileCopyUtils.copy(in, out);
            } catch (IOException e) {
                log.error("File [{}] upload failed", fileName, e);
                throw new RuntimeException(e);
            }
            FileInfo fileInfo = new FileInfo().setFileName(fileName).setUploadTime(currentTime);
            FILE_STORAGE.add(fileInfo);
        }
    }

    @Override
    public List<FileInfo> list() {
        return FILE_STORAGE;
    }

    @Override
    public Resource getFile(String fileName) {
        FILE_STORAGE.stream()
                .filter(info -> info.getFileName().equals(fileName))
                .findFirst()
                .orElseThrow(() -> new RuntimeException("File [ " + fileName + " ] not exist"));
        File file = new File(filePath + fileName);
        return new FileSystemResource(file);
    }
}

上传文件限制

可以在application.properties配置文件中限制上传单个文件的大小和所有文件的总大小,具体配置如下:

# 单个文件限制
spring.servlet.multipart.max-file-size=20MB
# 总大小限制
spring.servlet.multipart.max-request-size=100MB

测试验证

使用postman进行接口测试

文件上传

正常上传文件: 在这里插入图片描述 文件已存在: 在这里插入图片描述 上传超过20MB的文件: 在这里插入图片描述 上传总共超过100MB的文件: 在这里插入图片描述

查询文件列表

<img src="/Users/yuanzhihao/Library/Application Support/typora-user-images/image-20230328102117795.png" alt="image-20230328102117795" style="zoom:50%;" />

文件下载

正常文件下载: 在这里插入图片描述 下载不存在的文件: 在这里插入图片描述

代码中调用上传接口

主要整理了使用restTemplate和httpclient客户端如何在代码中调用文件上传接口。

使用restTemplate调用上传文件接口

@Test
public void uploadTestByRestTemplate() {
  HttpHeaders headers = new HttpHeaders();
  headers.setContentType(MediaType.MULTIPART_FORM_DATA);
  MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
  File file = new File("/Users/yuanzhihao/Downloads/mirrors-jenkins-master.zip");
  body.add("files", new FileSystemResource(file));
  body.add("files", new FileSystemResource(new File("/Users/yuanzhihao/Downloads/crictl-v1.22.0-linux-amd64.tar.gz")));
  body.add("files", new FileSystemResource(new File("/Users/yuanzhihao/Downloads/client(macosx).zip")));

  HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);

  String serverUrl = "http://localhost:8080/upload";
  RestTemplate restTemplate = new RestTemplate();
  ResponseEntity<String> response = restTemplate.postForEntity(serverUrl, requestEntity, String.class);
  System.out.println("Response code: " + response.getStatusCode() + " Response body: " + response.getBody());
}

使用httpclient调用上传文件接口

@Test
public void uploadTestByHttpClient() {
    File file = new File("/Users/yuanzhihao/Downloads/xzs-sql-v3.9.0.zip");
    FileBody fileBody = new FileBody(file, ContentType.DEFAULT_BINARY);

    MultipartEntityBuilder builder = MultipartEntityBuilder.create();
    builder.addPart("files", fileBody);

    HttpPost post = new HttpPost("http://localhost:8080/upload");
    org.apache.http.HttpEntity entity = builder.build();

    post.setEntity(entity);
    try (CloseableHttpClient client = HttpClientBuilder.create().build();
        CloseableHttpResponse response = client.execute(post)) {
        System.out.println("Response code: " + response.getStatusLine().getStatusCode());
        System.out.println("Response body: " + EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8));
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

结语

代码地址:https://github.com/yzh19961031/blogDemo/tree/master/uploadFile

参考:

https://spring.io/guides/gs/uploading-files/

https://www.baeldung.com/spring-rest-template-multipart-upload

https://www.bezkoder.com/spring-boot-file-upload/