MinIO 是一款基于Go语言发开的高性能、分布式的对象存储系统。MinIO 英文官网 MinIO 中文官网 注意:中文官方更新不及时,会有很多坑,请以英文官网为准。
项目中使用minio存放抓拍图片,按抓拍类型(人脸图片、车辆图片、报警图片)分为不同的桶bucket,服务器存储空间的原因,需要定期删除人脸、车辆图片,使用了removeObjects函数,后来发现可以设置minio 桶的生命周期BucketLifecycle存储天数,囧了各大囧。
各种操作参考java 客户端调用minio服务
以下是对项目中使用minio的java整理。
1、使用minio的包
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.3.7</version>
</dependency>
2、minio 客户端的创建
@Component
public class MinioClientManager implements InitializingBean {
/**
* 服务器查询地址,多个以逗号分开
*/
@Value("${minio.searchIp}")
private String searchIp;
/**
* 登录账号
*/
@Value("${minio.accessKey}")
private String accessKey;
/**
* 登录密码
*/
@Value("${minio.secretKey}")
private String secretKey;
public static Map<String, MinioClient> minioClientMap = new HashMap<>();
public OkHttpClient httpClient() {
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.retryOnConnectionFailure(true)
.callTimeout(10, TimeUnit.SECONDS)
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.build();
return okHttpClient;
}
@Override
public void afterPropertiesSet() {
String[] searchIps = searchIp.split(",");
for (String s : searchIps) {
MinioClient minioClient = MinioClient
.builder()
.endpoint(s)
.httpClient(httpClient())
.credentials(accessKey, secretKey)
.build();
minioClientMap.put(s, minioClient);
}
}
}
3、minio项目中用到的操作
@Component
public class MultiMinioTemplate implements InitializingBean {
/**
* 主服务器地址
*/
public static String nowIp;
@Value("${minio.ip}")
public void setNowIp(String nowIp) {
MultiMinioTemplate.nowIp = nowIp;
}
/**
* 服务器地址
*/
@Value("${minio.oldIp}")
private String oldIp;
/**
* 服务器查询地址,多个以逗号分开
*/
@Value("${minio.searchIp}")
private String searchIp;
/**
* 图片访问前缀
*/
@Value("${minio.accessUrlPrefix}")
private String accessUrlPrefix;
@Autowired
private MinioClientManager minioClientManager;
public MinioClient getMinioClientByIp(String ip) {
if (StringUtils.isBlank(ip)) {
ip = oldIp;
}
return minioClientManager.minioClientMap.get(ip);
}
public String getAccessUrlPrefix() {
return accessUrlPrefix;
}
/**
* 检查文件存储桶是否存在
*
* @param bucketName
* @return
*/
@SneakyThrows
public boolean bucketExists(String bucketName, String ip) {
BucketExistsArgs build = BucketExistsArgs.builder().bucket(bucketName).build();
return getMinioClientByIp(ip).bucketExists(build);
}
/**
* 创建bucket
*
* @param bucketName bucket名称
*/
@SneakyThrows
public void createBucket(String bucketName) {
if (bucketExists(bucketName, nowIp)) {
return;
}
getMinioClientByIp(nowIp).makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
/**
* 根据bucketName删除信息
*
* @param bucketName bucket名称
*/
@SneakyThrows
public void removeBucket(String bucketName, String ip) {
if (StringUtils.isBlank(ip)) {
String[] searchIps = searchIp.split(",");
for (String s : searchIps) {
if (this.bucketExists(s, bucketName)) {
RemoveBucketArgs build = RemoveBucketArgs.builder().bucket(bucketName).build();
getMinioClientByIp(s).removeBucket(build);
}
}
} else {
if (this.bucketExists(ip, bucketName)) {
RemoveBucketArgs build = RemoveBucketArgs.builder().bucket(bucketName).build();
getMinioClientByIp(ip).removeBucket(build);
}
}
}
/**
* 获取文件外链
*
* @param bucketName bucket名称
* @param objectName 文件名称
* @param expires 过期时间 <=7
* @return url
*/
@SneakyThrows
public String getObjectURL(String bucketName, String ip, String objectName, Integer expires) {
GetPresignedObjectUrlArgs build = GetPresignedObjectUrlArgs
.builder()
.bucket(bucketName)
.object(objectName)
.expiry(expires, TimeUnit.DAYS)
.method(Method.GET)
.build();
return getMinioClientByIp(ip).getPresignedObjectUrl(build);
}
/**
* 获取文件外链
*
* @param bucketName bucket名称
* @param objectName 文件名称
* @return url
*/
@SneakyThrows
public String getObjectURL(String bucketName, String ip, String objectName) {
return this.getObjectURL(bucketName, ip, objectName, 7);
}
/**
* 获取文件
*
* @param bucketName bucket名称
* @param objectName 文件名称
* @return 二进制流
*/
@SneakyThrows
public InputStream getObject(String bucketName, String ip, String objectName) {
GetObjectArgs build = GetObjectArgs
.builder()
.bucket(bucketName)
.object(objectName)
.build();
return getMinioClientByIp(ip).getObject(build);
}
/**
* 上传文件
*
* @param bucketName bucket名称
* @param objectName 文件名称
* @param stream 文件流
* @param size 大小
* @param contextType 类型
* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject
*/
public void putObject(String bucketName, String objectName, InputStream stream, long size, String contextType) throws Exception {
PutObjectArgs build = PutObjectArgs
.builder()
.bucket(bucketName)
.object(objectName)
.stream(stream, -1, 10 * 1024 * 1024)
.contentType(contextType)
.build();
getMinioClientByIp(nowIp).putObject(build);
}
/**
* 删除文件
*
* @param bucketName bucket名称
* @param objectName 文件名称
* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#removeObject
*/
public void removeObject(String bucketName, String ip, String objectName) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InternalException, ErrorResponseException, io.minio.errors.InternalException, XmlParserException, ServerException, InvalidResponseException {
RemoveObjectArgs build = RemoveObjectArgs
.builder()
.bucket(bucketName)
.object(objectName)
.build();
getMinioClientByIp(ip).removeObject(build);
}
/**
* 删除文件s
*
* @param bucketName bucket名称
* @param objectNames 文件名称
* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#removeObject
*/
public Iterable<Result<DeleteError>> removeObjects(String bucketName, String ip, List<String> objectNames) {
List<DeleteObject> objects = new LinkedList<>();
objectNames.forEach(objectName->objects.add(new DeleteObject(objectName)));
RemoveObjectsArgs build = RemoveObjectsArgs
.builder()
.bucket(bucketName)
.objects(objects)
.build();
return getMinioClientByIp(ip).removeObjects(build);
}
/**
* 列出某个存储桶中的所有对象。
*
* @param ip minio ip
* @param args 查询条件
* // Lists maximum 100 objects information with version whose names starts with 'E' and after
* // 'ExampleGuide.pdf'.
* Iterable<Result<Item>> results = minioClient.listObjects(
* ListObjectsArgs.builder()
* .bucket("my-bucketname")
* .startAfter("ExampleGuide.pdf")
* .prefix("E")
* .maxKeys(100)
* .includeVersions(true)
* .build());
*/
public Iterable<Result<Item>> listObjects(String ip, ListObjectsArgs args) throws XmlParserException {
return getMinioClientByIp(ip).listObjects( args);
}
public void setBucketLifecycle(String ip, SetBucketLifecycleArgs args)
throws ErrorResponseException, InsufficientDataException, io.minio.errors.InternalException,
InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException,
ServerException, XmlParserException {
getMinioClientByIp(ip).setBucketLifecycle(args);
}
public LifecycleConfiguration getBucketLifecycle(String ip, GetBucketLifecycleArgs args)
throws ErrorResponseException, InsufficientDataException, io.minio.errors.InternalException,
InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException,
ServerException, XmlParserException {
return getMinioClientByIp(ip).getBucketLifecycle(args);
}
public void deleteBucketLifecycle(String ip, DeleteBucketLifecycleArgs args)
throws ErrorResponseException, InsufficientDataException, io.minio.errors.InternalException,
InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException,
ServerException, XmlParserException {
getMinioClientByIp(ip).deleteBucketLifecycle(args);
}
@Override
public void afterPropertiesSet() {
}
}
4、生命周期的设置
public String setBucketLifeCycle(String ip,String bucketName,String ruleId,int expireDays,Boolean deleteMarker){
List<LifecycleRule> rules = new LinkedList<>();
rules.add(new LifecycleRule(Status.ENABLED,
null,
new Expiration((ZonedDateTime) null, expireDays, deleteMarker),
null,
ruleId,
null,
null,
null));
LifecycleConfiguration config = new LifecycleConfiguration(rules);
try {
minioTemplate.setBucketLifecycle(ip,SetBucketLifecycleArgs.builder().bucket(bucketName).config(config).build());
}catch (Exception e){
log.error("设置桶{}:{}生命周期异常", ip, bucketName, e);
return "错误类型:"+e.getClass().getName();
}
return "OK";
}
LifecycleRule 的配置参数
AbortIncompleteMultipartUpload(int daysAfterInitiation)设置分片在距最后修改时间30天后过期。
Expiration(ZonedDateTime date, Integer days, Boolean expiredObjectDeleteMarker) 指定日期或天数过期,标志删除or删除
RuleFilter(AndOperator andOperator,String prefix,Tag tag) 依据前缀删除(前缀即桶内的文件夹名)还是tag标志删除
id,一个桶可以设置多个rule
NoncurrentVersionExpiration(int noncurrentDays)设置非当前版本的Object
NoncurrentVersionTransition(int noncurrentDays,String storageClass) 设置非当前版本的Object距最后修改时间90天之后转为低频访问类型、归档类型。非当前版本对象何时进行存储类型的转换和转换的存储类型,待确认storageClass
一次可设置多个rule,第二次设置setBucketLifecycle会覆盖第一次设置setBucketLifecycle
5、生命周期获取、删除
//删除
minioTemplate.deleteBucketLifecycle(ip,DeleteBucketLifecycleArgs.builder()
.bucket(bucketName).build());
//获取
minioTemplate.getBucketLifecycle(ip,GetBucketLifecycleArgs.builder()
.bucket(bucketName).build())
还有三个参数设置,没明白怎么用 builder().region(String region) builder().extraHeaders(Map<String, String> headers) minioClient通过httpClicent访问,可设置http请求参数 builder().extraQueryParams(Map<String, String> queryParams)