Spring Boot 简明可用文件上传

目录

  • 总述
  • 项目结构
  • 一、Maven
  • 二、配置文件
    • 1. application.yml
    • 2. logback-spring.xml
  • 三、主要代码
    • 1. web 层
      • 1.1 controller UploadController
      • 1.2 AOP实现 ControllerAop
      • 1.3 统一返回结果 ResultBean
      • 1.4 服务状态码 ResultCode
      • 1.5 自定义异常 CheckException
    • 2. service 层
      • 2.1 文件服务类 FileService
      • 2.2 系统常量类 SystemConstant
    • 3. 数据库访问层
      • 3.1 repository FileRepository
      • 3.2 Entity UploadFile
  • 四、Postman 测试
    • 1. 上传文件
      • 1.1 设置 Headers
      • 1.2 设置 Body
    • 2. 查询文件

总述

使用 Spring boot 构建一个文件上传服务。本文主要内容

  1. 构建一个 Spring boot web 项目
  2. 单文件上传
  3. 使用 H2 数据库保存上传记录
  4. 使用 Postman 进行测试附件文件上传

项目结构

Spring Boot 简明可用文件上传_第1张图片

一、Maven


<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.0modelVersion>

    <groupId>org.examplegroupId>
    <artifactId>uploadartifactId>
    <version>1.0-SNAPSHOTversion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-parentartifactId>
        <version>2.2.6.RELEASEversion>
    parent>

    <properties>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <java.version>1.8java.version>
        <lombok.version>1.18.10lombok.version>
        <apache.math.version>3.6.1apache.math.version>
    properties>

    <dependencies>

        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>${lombok.version}version>
        dependency>

        <dependency>
            <groupId>ch.qos.logbackgroupId>
            <artifactId>logback-classicartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-jpaartifactId>
        dependency>

        <dependency>
            <groupId>com.h2databasegroupId>
            <artifactId>h2artifactId>
            <scope>runtimescope>
        dependency>
    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <configuration>
                    <source>${java.version}source>
                    <target>${java.version}target>
                configuration>
            plugin>
        plugins>
    build>
project>

二、配置文件

1. application.yml

# LOGGING
logging:
  config: classpath:logback-spring.xml

# file upload
dt.upload.root: d:\\upload\\ #上传存放根目录


spring:

  datasource:
    url: jdbc:h2:./test_db
    driverClassName: org.h2.Driver
    username: sa
    password: test

  jpa:
    show-sql: false
    database-platform: org.hibernate.dialect.H2Dialect
    hibernate.naming-strategy: org.hibernate.cfg.DefaultNamingStrategy
    hibernate.ddl-auto: update

server:
  port: 8080

2. logback-spring.xml


<configuration scan="true">

    <contextName>uploadcontextName>

    <property name="LOG_HOME" value="d:/log"/>
    <property name="FILE_DEBUG" value="upload.log"/>
    <property name="FILE_INFO" value="upload_info.log"/>

    <property name="CONSOLE_LOG_PATTERN"
              value="%highlight(%d{yyyy-MM-dd HH:mm:ss.SSS} %5.5level [%20.20t] %15.15logger{15} : %m) %n"/>

    <property name="FILE_LOG_PATTERN"
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} %5.5level [%20.20t] %30.30logger{29} : %m %n"/>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}pattern>
        encoder>
    appender>

    <appender name="FILE_NORMAL" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/${FILE_DEBUG}file>

        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">  
            
            <fileNamePattern>${LOG_HOME}/${FILE_DEBUG}-%d{yyyy-MM-dd}.%i.log.zipfileNamePattern>
                    <maxFileSize>50MBmaxFileSize>      
                    <maxHistory>60maxHistory>  
                    <totalSizeCap>1GBtotalSizeCap>  
        rollingPolicy>

        <encoder>
            <pattern>${FILE_LOG_PATTERN}pattern>
        encoder>
    appender>
    <appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/${FILE_INFO}file>

        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">  
            
            <fileNamePattern>${LOG_HOME}/${FILE_INFO}-%d{yyyy-MM-dd}.%i.log.zipfileNamePattern>
                    <maxFileSize>50MBmaxFileSize>      
                    <maxHistory>60maxHistory>  
                    <totalSizeCap>1GBtotalSizeCap>  
        rollingPolicy>

        <encoder>
            <pattern>${FILE_LOG_PATTERN}pattern>
        encoder>

        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFOlevel>
        filter>
    appender>

    <logger name="cn.dt.upload" level="debug" additivity="false">
        <appender-ref ref="FILE_NORMAL"/>
        <appender-ref ref="FILE_INFO"/>
        <appender-ref ref="STDOUT"/>
    logger>

    <root level="debug">
        <appender-ref ref="STDOUT"/>
    root>
configuration>

三、主要代码

1. web 层

1.1 controller UploadController

@Controller
public class UploadController {

    @Resource
    private FileService fileService;

    @RequestMapping(path = "/files", method = RequestMethod.GET)
    @ResponseBody
    public ResultBean findFiles() {
        return new ResultBean<>(fileService.findAllFiles());
    }

    @RequestMapping(path = "/files", method = RequestMethod.POST)
    @ResponseBody
    public ResultBean uploadFile(@RequestParam(value = "firmware") MultipartFile file,
                                     @RequestParam(value = "version") String version) throws IOException {
        return new ResultBean(this.fileService.uploadFile(file, version));
    }
}

1.2 AOP实现 ControllerAop

@Aspect
@Component
public class ControllerAop {

    private static final Logger logger = LoggerFactory.getLogger(ControllerAop.class);

    @Pointcut("execution(* cn.dt.upload.controller..*.*(..))")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object handlerControllerMethod(ProceedingJoinPoint pjp) {

        long startTime = System.currentTimeMillis();

        ResultBean<?> result;

        try {

            result = (ResultBean<?>) pjp.proceed();
            logger.info(pjp.getSignature() + "use time:" + (System.currentTimeMillis() - startTime));

        } catch (Throwable e) {
            result = handlerException(pjp, e);
        }

        return result;
    }

    /**
     * 封装异常信息,注意区分已知异常(自己抛出的)和未知异常
     */
    private ResultBean<?> handlerException(ProceedingJoinPoint pjp, Throwable e) {

        ResultBean<?> result = new ResultBean();

        result.setCode(FAIL);

        // 已知异常
        if (e instanceof CheckException) {
            result.setMsg(e.getLocalizedMessage());
        } else {
            logger.error(pjp.getSignature() + " error ", e);
            result.setMsg("系统错误,请稍后再试。");
        }

        return result;
    }
}

1.3 统一返回结果 ResultBean

@Data
public class ResultBean<T> implements Serializable {

    private static final long serialVersionUID = 1L;

    protected int code = SUCCESS;
    protected String msg = "执行成功";

    protected T data;

    public ResultBean() {
        super();
    }

    public ResultBean(T data) {
        super();
        this.data = data;
    }

    public ResultBean(Throwable e) {
        super();
        this.msg = e.toString();
        this.code = FAIL;
    }
}

1.4 服务状态码 ResultCode

public class ResultCode {
    public static final int NO_LOGIN = -1;
    public static final int SUCCESS = 0;
    public static final int FAIL = 1;
    public static final int NO_PERMISSION = 2;
}

1.5 自定义异常 CheckException

public class CheckException extends RuntimeException {

    public CheckException(String message) {
        super(message);
    }
}

2. service 层

2.1 文件服务类 FileService

@Service
@Slf4j
public class FileService {
    @Value("${dt.upload.root}")
    private String root = "";

    @Resource
    private FileRepository fileRepository;

    /**
     * 保存文件
     *
     * @param multipartFile 文件
     * @return 文件编号
     */
    public int uploadFile(MultipartFile multipartFile, String version) throws IOException {

        String originalFilename = multipartFile.getOriginalFilename();
        File   file             = new File(root, originalFilename);

        if (!file.exists()) {
            Files.createDirectories(file.getParentFile().toPath());
            Files.createFile(file.toPath());
        }

        byte[] fileBytes = multipartFile.getBytes();

        Files.write(file.toPath(), fileBytes, StandardOpenOption.WRITE);

        return saveFile(originalFilename, version, file, fileBytes, fileBytes.length).getId();
    }


    private UploadFile saveFile(String originalFilename, String version, File file, byte[] fileBytes, long size) {

        UploadFile uploadFile = fileRepository.findByVersion(version);

        if (uploadFile == null) {
            uploadFile = new UploadFile();
            uploadFile.setCreateTime(LocalDateTime.now(SystemConstant.CN_ZONE_ID));
        } else {
            uploadFile.setUpdateTime(LocalDateTime.now(SystemConstant.CN_ZONE_ID));
        }

        uploadFile.setFilePath(file.getPath());
        uploadFile.setFilename(originalFilename);
        uploadFile.setSize(size);

        uploadFile.setVersion(version);

        if (log.isDebugEnabled()) {
            log.debug("save firmware, {}", uploadFile);
        }

        return fileRepository.save(uploadFile);
    }

    public List<UploadFile> findAllFiles() {
        return fileRepository.findAllFiles();
    }
}

2.2 系统常量类 SystemConstant

public class SystemConstant {

    /**
     * 时区
     */
    public static final ZoneId CN_ZONE_ID = ZoneId.of("Asia/Shanghai");
}

3. 数据库访问层

3.1 repository FileRepository

public interface FileRepository extends CrudRepository<UploadFile, Integer> {

    /**
     * 查找所有的文件信息
     *
     * @return 文件列表
     */
    @Query("from UploadFile")
    List<UploadFile> findAllFiles();

    /**
     * 通过版本查询文件信息
     *
     * @param version 文件版本
     * @return 版本相同的文件列表
     */
    UploadFile findByVersion(String version);
}

3.2 Entity UploadFile

@Entity
@Table(name = "tab_files")
@Data
public class UploadFile {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;


    private String version;

    private String filename;
    private String filePath;
    private long size;

    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

四、Postman 测试

1. 上传文件

1.1 设置 Headers

Content-Type:multipart/form-data
Spring Boot 简明可用文件上传_第2张图片

1.2 设置 Body

Spring Boot 简明可用文件上传_第3张图片

2. 查询文件

Spring Boot 简明可用文件上传_第4张图片

你可能感兴趣的:(杂杂)