一、环境搭建
1.镜像拉取
docker pull minio/minio
2.创建容器:
docker run -d -p 9000:9000 --name=minio --restart=always -e "MINIO_ROOT_USER=admin" -e "MINIO_ROOT_PASSWORD=admin123456" -v /home/data:/data -v /home/config:/root/.minio minio/minio server /data --console-address ":9000" --address ":9090"
3.访问(账号:admin;密码:admin123456)
http://ip:9000
4.管理页面
创建桶:
二、快速入门
1.创建SpringBoot工程
2.导入依赖
<dependencies>
<!--minio-->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>7.1.0</version>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!--knife4j(swagger) -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.2</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
3.整合Swagger
1)编写Swagger配置类
package com.baidou.config;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* Swagger2配置类(生成接口文档)
*
* @author 白豆五
* @version 2023/04/21
* @since JDK8
*/
@Configuration
@EnableSwagger2 //开启swagger2注解支持
@EnableKnife4j //开启Knife4j注解支持
public class SwaggerConfig {
@Bean
public Docket webApiConfig() { //生成接口文档的清单
// 文档类型
return new Docket(DocumentationType.SWAGGER_2)
.groupName("webApi")
.apiInfo(webApiInfo())
.select()
//指定controller包扫描路径
.apis(RequestHandlerSelectors.basePackage("com.baidou.controller"))
.paths(PathSelectors.any())
.build();
}
//配置在线api文档信息
private ApiInfo webApiInfo() {
return new ApiInfoBuilder()
.title("网站-API文档")
.description("本文档描述了xxx管理系统微服务接口定义")
.version("1.0")
.contact(new Contact("白豆五", "https://blog.csdn.net/qq_46921028", "13212341234@163.com"))
.build();
}
}
2)设置静态资源映射(对Swagger的静态资源放行)
package com.baidou.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
// 对WebMvc进行一些配置,为swagger静态资源进行放行
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
// 静态资源放行
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 对swaggger静态资源放行
registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
4.编写 MinIO属性配置类
package com.baidou.dto;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.io.Serializable;
@Data
@Component
@ConfigurationProperties(prefix = "minio") //自动注入属性前缀为minio的配置
public class MinIOConfigProperties implements Serializable {
private String accessKey; // 访问key
private String secretKey; // 秘钥
private String bucket; // 桶
private String endpoint; // 地域节点
private String readPath; // 读取路径
}
5.编写MinIO配置类,注册MinioClient客户端的Bean对象
package com.baidou.config;
import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* MinIO配置类
*
* @author 白豆五
* @version 2023/04/21
* @since JDK8
*/
@Configuration
public class MinIOConfig {
@Autowired
private MinIOConfigProperties minIOConfigProperties;
// 注册MinIO实例
@Bean
public MinioClient buildMinioClient(){
return MinioClient
.builder()
.credentials(minIOConfigProperties.getAccessKey(), minIOConfigProperties.getSecretKey())
.endpoint(minIOConfigProperties.getEndpoint())
.build();
}
}
6.在application.yml文件中配置minio自定义属性和文件上传大小
minio:
accessKey: minio
secretKey: minio123
bucket: testminio
endpoint: http://192.168.200.128:9000
readPath: http://192.168.200.128:9000
servlet:
multipart:
# 单个上传文件的最大值是200mb
max-file-size: 200MB
# 单次请求的最大值
max-request-size: 200MB
7.编写操作minio相关业务接口
package com.baidou.service;
import java.io.InputStream;
/**
* 操作minio相关业务接口
*
* @author 白豆五
* @version 2023/04/21
* @since JDK8
*/
public interface FileStorageService {
/**
* 上传图片文件
*
* @param prefix 文件前缀
* @param filename 文件名
* @param inputStream 文件流
* @return 文件全路径
*/
public String uploadImgFile(String prefix, String filename, InputStream inputStream);
/**
* 上传html文件
*
* @param prefix 文件前缀
* @param filename 文件名
* @param inputStream 文件流
* @return 文件全路径
*/
public String uploadHtmlFile(String prefix, String filename, InputStream inputStream);
/**
* 删除文件
*
* @param pathUrl 文件全路径
*/
public void delete(String pathUrl);
/**
* 下载文件
*
* @param pathUrl 文件全路径
* @return
*/
public byte[] downLoadFile(String pathUrl);
}
业务实现类:
package com.baidou.service.impl;
import com.baidou.config.MinIOConfigProperties;
import com.baidou.service.FileStorageService;
import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
@Slf4j
@Service
public class MinIOFileStorageService implements FileStorageService {
@Autowired
private MinioClient minioClient;
@Autowired
private MinIOConfigProperties minIOConfigProperties;
private final static String separator = "/"; //文件夹分隔符
/**
* 构建文件的绝对路径
*
* @param dirPath 文件路径
* @param filename 文件名 yyyy/mm/dd/file.jpg
* @return /test
*/
public String builderFilePath(String dirPath, String filename) {
StringBuilder stringBuilder = new StringBuilder(50);
if (!StringUtils.isEmpty(dirPath)) {
stringBuilder.append(dirPath).append(separator);
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
String todayStr = sdf.format(new Date());
stringBuilder.append(todayStr).append(separator);
stringBuilder.append(filename);
return stringBuilder.toString();
}
/**
* 上传图片文件
*
* @param prefix 文件前缀
* @param filename 文件名
* @param inputStream 文件流
* @return 文件全路径
*/
@Override
public String uploadImgFile(String prefix, String filename, InputStream inputStream) {
String filePath = builderFilePath(prefix, filename);
try {
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.object(filePath)
.contentType("image/jpg")
.bucket(minIOConfigProperties.getBucket()).stream(inputStream, inputStream.available(), -1)
.build();
minioClient.putObject(putObjectArgs);
StringBuilder urlPath = new StringBuilder(minIOConfigProperties.getReadPath());
urlPath.append(separator + minIOConfigProperties.getBucket());
urlPath.append(separator);
urlPath.append(filePath);
return urlPath.toString();
} catch (Exception ex) {
log.error("minio put file error.", ex);
throw new RuntimeException("上传文件失败");
}
}
/**
* 上传html文件
*
* @param prefix 文件前缀
* @param filename 文件名
* @param inputStream 文件流
* @return 文件全路径
*/
@Override
public String uploadHtmlFile(String prefix, String filename, InputStream inputStream) {
String filePath = builderFilePath(prefix, filename);
try {
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.object(filePath) //文件名
.contentType("text/html")//文件类型
.bucket(minIOConfigProperties.getBucket())//桶名称与minio创建的桶一致
.stream(inputStream, inputStream.available(), -1)//文件流
.build();
minioClient.putObject(putObjectArgs);
StringBuilder urlPath = new StringBuilder(minIOConfigProperties.getReadPath());
urlPath.append(separator + minIOConfigProperties.getBucket());
urlPath.append(separator);
urlPath.append(filePath);
return urlPath.toString(); //文件全路径
} catch (Exception ex) {
log.error("minio put file error.", ex);
ex.printStackTrace();
throw new RuntimeException("上传文件失败");
}
}
/**
* 删除文件
*
* @param pathUrl 文件全路径
*/
@Override
public void delete(String pathUrl) {
String key = pathUrl.replace(minIOConfigProperties.getEndpoint() + "/", "");
int index = key.indexOf(separator);
String bucket = key.substring(0, index);
String filePath = key.substring(index + 1);
// 删除Objects
RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder().bucket(bucket).object(filePath).build();
try {
minioClient.removeObject(removeObjectArgs);
} catch (Exception e) {
log.error("minio remove file error. pathUrl:{}", pathUrl);
e.printStackTrace();
}
}
/**
* 下载文件
*
* @param pathUrl 文件全路径
* @return 文件流
*/
@Override
public byte[] downLoadFile(String pathUrl) {
String key = pathUrl.replace(minIOConfigProperties.getEndpoint() + "/", "");
int index = key.indexOf(separator);
String bucket = key.substring(0, index);
String filePath = key.substring(index + 1);
InputStream inputStream = null;
try {
inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(minIOConfigProperties.getBucket()).object(filePath).build());
} catch (Exception e) {
log.error("minio down file error. pathUrl:{}", pathUrl);
e.printStackTrace();
}
//字节数组输出流
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buff = new byte[100];
int rc = 0;
while (true) {
try {
if (!((rc = inputStream.read(buff, 0, 100)) > 0)) break;
} catch (IOException e) {
e.printStackTrace();
}
byteArrayOutputStream.write(buff, 0, rc);
}
return byteArrayOutputStream.toByteArray();
}
}
8.编写统一结果处理类
package com.baidou.dto;
import lombok.Data;
/**
* 统一结果处理类
*
* @author 白豆五
* @version 2023/04/21
* @since JDK8
*/
@Data
public class Result<T> {
private Integer code; //响应状态码
private String msg; //响应消息
private T data; //响应数据
/**
* 处理成功的返回结果
*
* @param data 数据
* @param <T>
* @return R<T>
*/
public static <T> Result<T> success(T data, String msg) {
Result<T> r = new Result<>();
r.setCode(200);
r.setData(data);
r.setMsg(msg);
return r;
}
/**
* 处理失败的返回结果
*
* @param msg 错误信息
* @param <T>
* @return R<T>
*/
public static <T> Result<T> error(String msg) {
Result<T> r = new Result<>();
r.setCode(503);
r.setMsg(msg);
return r;
}
}
9.编写controller
package com.baidou.controller;
import com.baidou.dto.Result;
import com.baidou.service.FileStorageService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
/**
* 操作minio的控制器类
*
* @author 白豆五
* @version 2023/04/21
* @since JDK8
*/
@RestController
@RequestMapping("/minio")
@Api(tags = "minio相关接口")
public class MinioController {
@Autowired
private FileStorageService fileStorageService;
/**
* 上传图片到minio
*
* @param file
* @return
*/
@PostMapping("upload")
@ApiOperation(value = "图片上传接口")
public Result uploadFile(MultipartFile file) throws IOException {
try {
// 获取文件名称
String fileName = file.getOriginalFilename();
/*解决多次上传同名文件覆盖问题*/
// 在文件名称里面添加随机唯一的值
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
fileName = uuid + fileName;
// 获取文件输入流
InputStream is = file.getInputStream();
String imgUrl = fileStorageService.uploadImgFile("img", fileName, is);
return Result.success(imgUrl, "上传成功");
} catch (IOException e) {
e.printStackTrace();
return Result.error("上传失败");
}
}
}