使用 Spring boot 构建一个文件上传服务。本文主要内容
<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>
# 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
<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>
@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));
}
}
@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;
}
}
@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;
}
}
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;
}
public class CheckException extends RuntimeException {
public CheckException(String message) {
super(message);
}
}
@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();
}
}
public class SystemConstant {
/**
* 时区
*/
public static final ZoneId CN_ZONE_ID = ZoneId.of("Asia/Shanghai");
}
public interface FileRepository extends CrudRepository<UploadFile, Integer> {
/**
* 查找所有的文件信息
*
* @return 文件列表
*/
@Query("from UploadFile")
List<UploadFile> findAllFiles();
/**
* 通过版本查询文件信息
*
* @param version 文件版本
* @return 版本相同的文件列表
*/
UploadFile findByVersion(String version);
}
@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;
}
Content-Type:multipart/form-data