Java SpringBoot实现文件上传下载

1. 业务概述

业务开发中文件上传与下载在Web应用中是一个比较常见的功能。通常采用两张方式:
1.上传至自定义的文件服务器。
2.上传至云服务器,例如阿里云OSS,AWS等
阿里云OSS:https://help.aliyun.com/product/31815.html?spm=a2c4g.750001.list.24.7a184c07xKtHUD
AWS S3:https://aws.amazon.com/cn/s3/

云服务相关文档很详细就不在介绍,本文主要是springboot实现文件的上传下载功能。

2. 环境准备

JDK: Java 1.8
Framework: Spring Boot 2.5.0(Only Using Spring Web MVC)
Maven: Maven 3.5.0+
IDE: IntelliJ IDEA 2019.2
springfox-swagger2 2.6.1

3. 实现验证

工程目录结构说明如下:

config/FileUploadConfiguration.java: 常规组件,主要在重启应用时清理历史文件;
controller/FileUploadController.java: 主要的控制器,负责处理文件的上传,下载,浏览等请求;
exception/FileUploadExceptionAdvice.java: 全局的异常处理类,提供用户友好的异常提示信息;
service/FileStorageService.java: 文件上传接口类,提供存储地址初始化,保存文件,加载文件,清理文件等操作;
service/impl/FileStorageServiceImpl.java: 文件上传接口实现类;
valueobject/UploadFile.java: 封装了文件名和存储地址的POJO类;
valueobject/Message.java: 请求/响应的消息对象;
resources/application.yml: 项目配置文件,主要配置了文件上传大小限制;
pom.xml:Maven依赖配置文件。

3.1 文件对象 UploadFile

package com.example.category.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 文件对象
 *
 * @author zrj
 * @since 2021/8/17
 **/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UploadFile {
    private String fileName;
    private String url;
}

3.2 文件上传接口 FileStorageService

package com.example.category.service;

import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;

import java.nio.file.Path;
import java.util.stream.Stream;

/**
 * 文件上传接口
 *
 * @author zrj
 * @since 2021/8/17
 **/
public interface FileStorageService {
    /**
     * 文件初始化
     */
    void init();

    /**
     * 文件上传
     */
    void fileUpload(MultipartFile multipartFile);

    /**
     * 文件下载
     */
    Resource load(String filename);

    /**
     * 文件加载
     */
    Stream<Path> load();

    /**
     * 文件全部删除
     */
    void clear();

    /**
     * 文件删除
     */
    void fileDeleted(String fileName);
}

3.3 文件上传实现类 FileStorageServiceImpl

package com.example.category.service.impl;

import com.example.category.service.FileStorageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.FileSystemUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.core.io.Resource;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;

/**
 * 文件上传实现类
 *
 * @author zrj
 * @since 2021/8/17
 **/
@Slf4j
@Service("fileStorageService")
public class FileStorageServiceImpl implements FileStorageService {
    private final Path path = Paths.get("fileStorage");

    @Override
    public void init() {
        log.info("文件管理初始化...path=" + path);
        try {
            boolean exists = new File(path.toUri()).exists();
            if (!exists) {
                log.info("文件路径不存在创建");
                Files.createDirectory(path);
            }
        } catch (IOException e) {
            throw new RuntimeException("Could not initialize folder for upload!");
        }
    }

    @Override
    public void fileUpload(MultipartFile multipartFile) {
        log.info("文件上传...path=" + path);
        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) {
        log.info("文件下载...path=" + path);

        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() {
        log.info("文件下载...path=" + path);
        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() {
        log.info("文件clear...path=" + path);
        if (false) {
            FileSystemUtils.deleteRecursively(path.toFile());
        }
    }

    @Override
    public void fileDeleted(String fileName) {
        log.info("文件删除...fileName=" + fileName);
    }
}

3.4 文件上传初始化配置 FileUploadConfiguration

package com.example.category.config;

import com.example.category.service.FileStorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Service;

/**
 * 文件上传初始化配置
 *
 * @author zrj
 * @since 2021/8/17
 **/
@Service
public class FileUploadConfiguration implements CommandLineRunner {

    @Autowired
    FileStorageService fileStorageService;

    @Override
    public void run(String... args) throws Exception {
        fileStorageService.clear();
        fileStorageService.init();
    }
}

3.5 文件控制器 FileUploadController


3.6 统一结果集 Response

package com.example.category.controller;

import com.example.category.entity.Response;
import com.example.category.entity.UploadFile;
import com.example.category.service.FileStorageService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 文件控制器
 *
 * @author zrj
 * @since 2021/8/17
 **/
@RestController
@RequestMapping("/file")
@Api(tags = "文件管理", description = "文件上传")
public class FileUploadController {
    @Autowired
    FileStorageService fileStorageService;

    /**
     * 文件上传
     */
    @PostMapping("/fileUpload")
    @ApiOperation(value = "文件上传", httpMethod = "POST")
    public Response<String> fileUpload(@RequestParam("file") MultipartFile file) {
        try {
            fileStorageService.fileUpload(file);
            return Response.success("fileUpload success", file.getOriginalFilename());
        } catch (Exception e) {
            return Response.success("fileUpload failed", file.getOriginalFilename());
        }
    }

    /**
     * 文件列表
     */
    @GetMapping("/fileList")
    @ApiOperation(value = "文件列表", httpMethod = "GET")
    public Response<List<UploadFile>> fileList() {
        List<UploadFile> fileList = 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 Response.success("fileList success", fileList);
    }

    /**
     * 文件下载
     * {filename:.+}:正则表达式匹配, 语法: {varName:regex} 前面是变量名,后面是表达式。
     * 匹配出现过一次或多次.的字符串 如: "hello.png"
     */
    @GetMapping("/files/{filename:.+}")
    @ApiOperation(value = "文件下载", httpMethod = "GET")
    public ResponseEntity<Resource> getFile(@PathVariable("filename") String filename) {
        // 根据文件名读取文件
        Resource file = fileStorageService.load(filename);

        // @ResponseBody 用于直接返回结果(自动装配)
        // ResponseEntity 可以定义返回的 HttpHeaders 和 HttpStatus (手动装配)
        // ResponseEntity.ok 相当于设置 HttpStatus.OK (200)
        // CONTENT_DISPOSITION 该 标志将通知浏览器启动下载
        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=\"" + file.getFilename() + "\"")
                .body(file);
    }

    /**
     * 文件删除
     */
    @PostMapping("/fileDeleted")
    @ApiOperation(value = "文件删除", httpMethod = "POST")
    public Response<String> fileDeleted(@RequestParam("fileName") String fileName) {
        try {
            fileStorageService.fileDeleted(fileName);
            return Response.success("fileDeleted success", fileName);
        } catch (Exception e) {
            return Response.success("fileDeleted failed", fileName);
        }
    }
}

3.7 配置文件 application.yml

spring.servlet.multipart.max-request-size=50MB: 单次请求所能上传文件的总文件大小
spring.servlet.multipart.max-file-size=50MB:单个文件所能上传的文件大小

3.8 全局异常处理 FileUploadExceptionAdvice

package com.example.category.exception;

import com.example.category.entity.Message;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import javax.naming.SizeLimitExceededException;

/**
 * @author zrj
 * @since 2021/8/17
 **/
@ControllerAdvice
public class FileUploadExceptionAdvice extends ResponseEntityExceptionHandler {

    @ExceptionHandler(SizeLimitExceededException.class)
    public ResponseEntity<Message> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
        return ResponseEntity.badRequest().body(new Message("Upload file too large."));
    }
}

4. 验证

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值