项目1:SSM + Layui + Mysql8 公司测评系统
项目2:SpringBoot+Vue+ Mysql8 大学社团管理系统
项目3:搭建自用单点OOS服务
项目4:搭建一套自己的验证授权及资源访问服务
本篇文章记录了搭建自用单点OOS服务的过程,避坑记录等。采用SpingBoot自定义starter形式,构建自己的文件存储服务。主要涉及到的技术有:SpringBoot、Docker 、MinIo。
详细见我的另外一片文章。https://blog.csdn.net/qq_38020915/article/details/117386512
starter
starter
命名规范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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.5.0version>
<relativePath/>
parent>
<groupId>com.dingwengroupId>
<artifactId>dingwen-minio-starterartifactId>
<version>1.0-SNAPSHOTversion>
<name>dingwen-minio-startername>
<description>dingwen-minio-starterdescription>
<properties>
<java.version>1.8java.version>
<minio.version>7.0.2minio.version>
<commons.io.version>2.6commons.io.version>
<spring.boot.version>2.4.1spring.boot.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>${spring.boot.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>io.miniogroupId>
<artifactId>minioartifactId>
<version>${minio.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-autoconfigureartifactId>
dependency>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>${commons.io.version}version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-javadoc-pluginartifactId>
<version>3.0.1version>
<configuration>
<encoding>UTF-8encoding>
<charset>UTF-8charset>
<docencoding>UTF-8docencoding>
<doclint>nonedoclint>
configuration>
<executions>
<execution>
<id>attach-javadocsid>
<phase>packagephase>
<goals>
<goal>jargoal>
goals>
execution>
executions>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-source-pluginartifactId>
<version>3.0.0version>
<configuration>
<attach>trueattach>
configuration>
<executions>
<execution>
<phase>compilephase>
<goals>
<goal>jargoal>
goals>
execution>
executions>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>2.3.1version>
<configuration>
<source>1.8source>
<target>1.8target>
configuration>
plugin>
plugins>
build>
project>
注意:引入spring-boot-autoconfigure与spring-boot-autoconfigure-processor(xml、properties)来加载配置
package com.dingwen.minsta.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* minio 配置类
*
* @author dingwen
* 2021.05.25 17:05
*/
@Getter
@Setter
@ConfigurationProperties(prefix = "dingwen.minio")
public class DingwenMinioStarterProperties {
/**
* 一天的毫秒数
*/
private static final Long DAY_MILLISECOND = 1000L * 60 * 60 * 24;
/**
* 临时缓存目录
*/
private static final String TEMP_DIR = "." + File.separator + "temp" + File.separator;
/**
* 访问minio服务URL
*/
private String endPoint;
/**
* 存储桶
*/
private String bucket;
/**
* 访问密钥
*/
private String accessKey;
/**
* 秘密密钥
*/
private String secretKey;
/**
* 临时dir
*/
private String tempDir = TEMP_DIR;
/**
* 是否进行缓存清理
*/
private boolean tempCleanFlag = true;
/**
* 第一次临时清理时间
*/
private Date tempCleanFirstTime;
/**
* 临时清洁周期(延时时间)
*/
private Long tempCleanPeriod;
/**
* 临时清洁时间(间隔时间)
*/
private Long tempCleanDuration;
public void setTempCleanFirstTime(String tempCleanFirstTime) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
try {
this.tempCleanFirstTime = format.parse(tempCleanFirstTime);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
package com.dingwen.minsta.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* 自动配置类
*
* @author dingwen
* 2021.05.25 17:03
*/
@Configuration
@EnableConfigurationProperties(DingwenMinioStarterProperties.class)
public class DingwenMinioStarterAutoConfiguration {
}
@Configuration: 告诉Spring该类是配置类 @EnableConfigurationProperties(DingwenMinioStarterProperties.class)
:配置映射
由于
starter
工程和service
工程包路径不一致了,是两个工程@Configuration注解标注的类扫描不到无法加入IOC容器,需要在此处配置。
SpringBoot 启动类默认的@SpringBootApplication注解中默认包含了@EnableAutoConfiguration注解,启动时就能扫描本项目下被@Configuration注解修饰的类并将其注入IOC容器。而对于引入的starter项目来说,@Configuration不能在本项目中扫描到,是无法加载的。所以需要启动时读取/META-INF/spring.factories文件,然后读取org.springframework.boot.autoconfigure.EnableAutoConfiguration指向的由@Configuration注解修饰的类并将其注入IOC容器。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.dingwen.minsta.config.DingwenMinioStarterAutoConfiguration
banner
到工具网站生成
banner
新建txt
文件。http://patorjk.com/software/taag/#p=display&f=Graffiti&t=Type%20Something%20
${AnsiColor.BRIGHT_CYAN} # 指定颜色
${application.version}) # 获取版本信息
${spring-boot.version} # 获取版本信息
mvn clean install
https://gitee.com/dingwen-gitee/dingwen-minio-starter
service
SpringBoot
项目
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.5.0version>
<relativePath/>
parent>
<groupId>com.dingwengroupId>
<artifactId>dingwen-minio-serviceartifactId>
<version>1.0.0-SNAPSHOTversion>
<name>dingwen-minio-servicename>
<description>dingwen-minio-servicedescription>
<properties>
<java.version>1.8java.version>
<fastjson.version>1.2.75fastjson.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>com.dingwengroupId>
<artifactId>dingwen-minio-starterartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>${fastjson.version}version>
dependency>
<dependency>
<groupId>com.github.xiaoymingroupId>
<artifactId>knife4j-spring-boot-starterartifactId>
<version>2.0.7version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
package com.dingwen.minser;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackages = "com.dingwen")
public class DingwenMinioServiceApplication {
public static void main(String[] args) {
SpringApplication.run(DingwenMinioServiceApplication.class, args);
}
}
dingwen:
minio:
end-point: http://192.168.233.129:9000
bucket: image
access-key: admin
secret-key: 1234567890
temp-dir: D:\temp\
temp-clean-flag: true
temp-clean-period: 12960000
temp-clean-duration: 12960000
temp-clean-first-time: 2020-1-17 11:45:00
spring:
servlet:
multipart:
enabled: true
max-file-size: 2048MB # 单个文件最大限制
max-request-size: 3000MB # 整体请求大小限制
package com.dingwen.minser.controller;
import ch.qos.logback.core.util.FileUtil;
import com.dingwen.minser.exception.ServiceException;
import com.dingwen.minser.result.Result;
import com.dingwen.minser.result.ResultGenerator;
import com.dingwen.minsta.service.FileService;
import io.swagger.annotations.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
* entrance controller
*
* @author dingwen
* 2021.05.28 14:07
*/
@RestController
@RequestMapping("/minio")
@Api(value = "minio 服务", tags = "minio")
public class EntranceController {
/**
* 文件服务
*/
private final FileService fileService;
@Autowired
EntranceController(FileService fileService) {
this.fileService = fileService;
}
/**
* 创建bucket
*
* @return {@link Result}
*/
@ApiOperation("创建bucket")
@ApiImplicitParam(value = "存储桶名称", name = "bucketName", required = true)
@PostMapping("/bucket/add")
public Result add(@RequestParam("bucketName") String bucketName) {
boolean flag = fileService.createBucket(bucketName);
return flag ? ResultGenerator.genOkResult(bucketName + "创建成功")
: ResultGenerator.genFailureResult(bucketName + "创建失败");
}
/**
* 删除
*
* @param bucketName bucket名称
* @return {@link Result}
*/
@ApiOperation("删除bucket")
@ApiImplicitParam(value = "存储桶名称", name = "bucketName", required = true)
@DeleteMapping("/bucket/remove")
public Result remove(@RequestParam("bucketName") String bucketName) {
boolean flag = fileService.removeBucket(bucketName);
return flag ? ResultGenerator.genOkResult(bucketName + "删除成功")
: ResultGenerator.genFailureResult(bucketName + "删除失败");
}
/**
* 上传
*
* @param multipartFile 文件
* @param bucketName bucket名称
* @param destinationFileName 目标文件的名字
* @return {@link Result}
*/
@ApiOperation("文件上传")
@ApiImplicitParams(
{
@ApiImplicitParam(value = "文件", required = true, name = "multipartFile"),
@ApiImplicitParam(value = "存储桶名称", required = true, name = "bucketName"),
@ApiImplicitParam(value = "目标文件名", required = false, name = "destinationFileName")
}
)
@PostMapping("/file/upload")
public Result upload(@RequestParam(value = "multipartFile", required = true) MultipartFile multipartFile,
@RequestParam(value = "bucketName", required = true) String bucketName,
@RequestParam(value = "destinationFileName", required = false) String destinationFileName) {
try {
String fileName = null;
if (!StringUtils.hasText(destinationFileName)
|| ObjectUtils.isEmpty(destinationFileName)
|| destinationFileName.equalsIgnoreCase("null")) {
destinationFileName = multipartFile.getOriginalFilename();
}
fileName = fileService.saveFile(bucketName, new ByteArrayInputStream(multipartFile.getBytes()), destinationFileName);
return StringUtils.hasText(fileName) ? ResultGenerator.genOkResult("文件已成功上传,文件名:" + fileName) :
ResultGenerator.genFailureResult("文件上传失败").setCode(Integer.parseInt(HttpStatus.INTERNAL_SERVER_ERROR.name()));
} catch (Exception e) {
e.printStackTrace();
throw new ServiceException("文件上传失败:{}", e.getMessage());
}
}
/**
* 下载
*
* @param response 响应
* @param bucketName bucket名称
* @param fileName 文件名称
* @return {@link ResponseEntity}
*/
@ApiOperation("文件下载")
@ApiImplicitParams(
{
@ApiImplicitParam(value = "存储桶名称", name = "bucketName"),
@ApiImplicitParam(value = "文件名称", name = "fileName"),
}
)
@GetMapping("/file/download")
public ResponseEntity<Resource> download(
HttpServletResponse response,
@RequestParam("bucketName") String bucketName,
@RequestParam("fileName") String fileName
) throws UnsupportedEncodingException {
fileName = fileName.trim();
response.setContentType(MediaType.MULTIPART_FORM_DATA_VALUE);
// 文件名中文乱码解决
response.addHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes(StandardCharsets.UTF_8), "ISO8859-1"));
try {
fileService.write(fileName, bucketName, response.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
throw new ServiceException("文件下载异常:{}", e.getMessage());
}
return null;
}
/**
* 文件删除
*
* @param fileName 文件名称
* @param bucketName bucket名称
* @return {@link Result}
*/
@ApiOperation("删除文件")
@ApiImplicitParams(
{
@ApiImplicitParam(value = "文件名称", name = "fileName"),
@ApiImplicitParam(value = "存储桶名称", name = "bucketName")
}
)
@DeleteMapping("file/remove")
public Result remove(@RequestParam("fileName") String fileName, @RequestParam(value = "bucketName", required = false) String bucketName) {
boolean flag = false;
if (StringUtils.hasText(bucketName)) {
flag = fileService.removeFile(bucketName, fileName);
} else {
flag = fileService.removeFile(fileName);
}
return flag ? ResultGenerator.genOkResult("删除文件成功") : ResultGenerator.genFailureResult("删除文件失败");
}
}
https://gitee.com/dingwen-gitee/dingwen-minio-service
banner
MinIo
服务