简介
- MinIO 是在 Apache License v2.0 下发布的对象存储服务器。 它与 Amazon S3 云存储服务兼容。 它最适合存储非结构化数据,如照片,视频,日志文件,备份和容器/ VM 映像。 对象的大小可以从几 KB 到最大 5TB。
- MinIO 服务器足够轻,可以与应用程序堆栈捆绑在一起,类似于 NodeJS,Redis 和 MySQL
- 一种高性能的分布式对象存储服务器,用于大型数据基础设施。它是机器学习和其他大数据工作负载下Hadoop HDFS 的理想 s3 兼容替代品
安装(2021-04-18T19-26-29Z)
docker pull minio/minio:RELEASE.2021-04-18T19-26-29Z
运行MinIO 自定义 Access(至少三位) 和 Secret (至少8位)密钥要覆盖 MinIO 的自动生成的密钥,将 Access 和 Secret 密钥设为环境变量。MinIO 允许常规字符串作为 Access 和 Secret 密钥
docker run -p 9000:9000 --name=cg_minio -d --restart=always -e "MINIO_ACCESS_KEY=cgadmin" -e "MINIO_SECRET_KEY=cgadmin2022" -v /root/minio/data:/data -v /root/minio/config:/root/.minio minio/minio:RELEASE.2021-04-18T19-26-29Z server /data
登录:
http://192.168.220.130:9000
springboot整合minio
导入依赖
<!--minio-->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.0.3</version>
</dependency>
修改配置文件
spring:
# 配置文件上传大小限制
servlet:
multipart:
max-file-size: 200MB
max-request-size: 200MB
minio:
endpoint: http://192.168.220.110:9000
accessKey: cgadmin
secretKey: cgadmin2022
secure: false
imgSize: 1024 # 图片大小限制,单位:m
fileSize: 1024 # 文件大小限制,单位:m
shareBucketName: pet-photo-bucket
videoShareBucketName: pet-video-bucket
# 雪花算法配置
code:
worker_id: 2
datacenter_id: 22
配置实体类
@Component
@ConfigurationProperties(prefix = "minio")
@Data
public class MinioProperties{
/**
* 是否开启
*/
private Boolean enabled;
/**
* 存储对象服务器类型
*/
private OssType type;
/**
* OSS 访问端点,集群时需提供统一入口
*/
private String endpoint;
/**
* 用户名
*/
private String accessKey;
/**
* 密码
*/
private String secretKey;
}
存储对象服务器枚举
@Getter
@AllArgsConstructor
public enum OssType {
/**
* Minio 对象存储
*/
MINIO("minio", 1),
/**
* 华为 OBS
*/
OBS("obs", 2),
/**
* 腾讯 COS
*/
COS("tencent", 3),
/**
* 阿里巴巴 SSO
*/
ALIBABA("alibaba", 4),
;
/**
* 名称
*/
final String name;
/**
* 类型
*/
final int type;
}
配置类
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({MinioClient.class})
@EnableConfigurationProperties({MinioProperties.class, CodeProperties.class})
@ConditionalOnExpression("${minio.enabled}")
@ConditionalOnProperty(value = "minio.type", havingValue = "minio")
public class MinioConfiguration {
@Bean
@SneakyThrows
@ConditionalOnMissingBean(MinioClient.class)
public MinioClient minioClient(MinioProperties minioProperties) {
return MinioClient.builder()
.endpoint(minioProperties.getEndpoint())
.credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey())
.build();
}
@Bean
@ConditionalOnBean({MinioClient.class})
@ConditionalOnMissingBean(MinioUtil.class)
public MinioUtil minioUtil(MinioClient minioClient, MinioProperties minioProperties, CodeProperties codeProperties) {
return new MinioUtil(minioClient, minioProperties,codeProperties);
}
}
工具类
@Slf4j
@AllArgsConstructor
public class MinioUtil {
/**
* MinIO 客户端
*/
private MinioClient minioClient;
/**
* MinIO 配置类
*/
private MinioProperties minioProperties;
/**
* 订单号
*/
private CodeProperties codeProperties;
/**
* 查询所有存储桶
*
* @return Bucket 集合
*/
@SneakyThrows
public List<Bucket> listBuckets() {
return minioClient.listBuckets();
}
/**
* 桶是否存在
*
* @param bucketName 桶名
* @return 是否存在
*/
@SneakyThrows
public boolean bucketExists(String bucketName) {
return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
}
/**
* 创建存储桶
*
* @param bucketName 桶名
*/
@SneakyThrows
public void makeBucket(String bucketName) {
if (!bucketExists(bucketName)) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
}
/**
* 删除一个空桶 如果存储桶存在对象不为空时,删除会报错。
*
* @param bucketName 桶名
*/
@SneakyThrows
public void removeBucket(String bucketName) {
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
}
/**
* description: 上传文件
*
* @author: CG
* @date: 2022/4/20
* @params:* @Param multipartFile:
* @Param bucketName:
* @return:* @return: java.lang.String
*/
@SneakyThrows
public String pullObject(MultipartFile multipartFile, String bucketName) {
String fileExt = FileUtil.extName(multipartFile.getOriginalFilename());
String uuidFileName = generateOssUuidFileName(fileExt);
InputStream inputStream = multipartFile.getInputStream();
String fileType = multipartFile.getContentType();
System.out.println(fileExt + "--" + fileType);
try {
Assert.isTrue(bucketExists(bucketName), "桶不存在");
minioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(uuidFileName)
.stream(inputStream, inputStream.available(), -1)
.contentType(fileType)
.build());
return StrUtil.SLASH + bucketName + StrUtil.SLASH + uuidFileName;
} finally {
inputStream.close();
}
}
/**
* 返回临时带签名、过期时间一天、Get请求方式的访问URL
*
* @param bucketName 桶名
* @param ossFilePath Oss文件路径
* @return
*/
@SneakyThrows
public String getPresignedObjectUrl(String bucketName, String ossFilePath) {
return minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(bucketName)
.object(ossFilePath)
.expiry(60 * 60 * 24)
.build());
}
/**
* GetObject接口用于获取某个文件(Object)。此操作需要对此Object具有读权限。
*
* @param bucketName 桶名
* @param ossFilePath Oss文件路径
*/
@SneakyThrows
public InputStream getObject(String bucketName, String ossFilePath) {
return minioClient.getObject(
GetObjectArgs.builder().bucket(bucketName).object(ossFilePath).build());
}
/**
* 查询桶的对象信息
*
* @param bucketName 桶名
* @param recursive 是否递归查询
* @return
*/
@SneakyThrows
public Iterable<Result<Item>> listObjects(String bucketName, boolean recursive) {
return minioClient.listObjects(
ListObjectsArgs.builder().bucket(bucketName).recursive(recursive).build());
}
/**
* 生成随机文件名,防止重复
*
* @return
*/
public String generateOssUuidFileName(String fileExt) {
Snowflake snowflake = IdUtil.getSnowflake(codeProperties.getWorkerId(), codeProperties.getDatacenterId());
String resourceCodeNum = snowflake.nextIdStr();
return
DateUtil.format(new Date(), "yyyy/MM/dd") +
StrUtil.SLASH +
resourceCodeNum +
StrUtil.C_DOT +
fileExt;
}
/**
* 获取带签名的临时上传元数据对象,前端可获取后,直接上传到Minio
*
* @param bucketName
* @param fileName
* @return
*/
@SneakyThrows
public Map<String, String> getPresignedPostFormData(String bucketName, String fileName) {
// 为存储桶创建一个上传策略,过期时间为7天
PostPolicy policy = new PostPolicy(bucketName, ZonedDateTime.now().plusDays(7));
// 设置一个参数key,值为上传对象的名称
policy.addEqualsCondition("key", fileName);
// 添加Content-Type以"image/"开头,表示只能上传照片
policy.addStartsWithCondition("Content-Type", "image/");
// 设置上传文件的大小 64kiB to 10MiB.
policy.addContentLengthRangeCondition(64 * 1024, 10 * 1024 * 1024);
return minioClient.getPresignedPostFormData(policy);
}
}
service
public void upResource(String resourceDescribe, MultipartFile multipartFile, String bucketName) {
String resourcePath = minioUtil.pullObject(multipartFile, bucketName);
Assert.notEmpty(resourcePath, "资源上传失败");
}
controller
@PostMapping(value = "{bucket-name}", headers = "content-type=multipart/form-data")
@ApiOperation(value = "资源上传", response = ResultDTO.class)
@ApiImplicitParams({
@ApiImplicitParam(value = "资源描述", name = "resourceDescribe", required = true, paramType = "query", dataType = "String"),
@ApiImplicitParam(value = "minio桶名称", name = "bucket-name", required = true, paramType = "path", dataType = "String"),
})
public ResultDTO<ResourceUpDTO> resourceUp(@NotNull String resourceDescribe, @NotNull @ApiParam(name = "multipartFile", value = "资源文件", required = true) MultipartFile multipartFile, @PathVariable("bucket-name") @NotNull String bucketName) {
service.upResource(resourceDescribe, multipartFile, bucketName);
return ResultDTO.success();
}