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)
minio-java
Minio 提供了多种语言的SDK,比如java、go、python等。JAVA开发平台可以选择JS和java SDK,也就是前端和后端都可以直接集成minio。
每个OSS的用户都会用到上传服务。Web端常见的上传方法是用户在浏览器或App端上传文件到应用服务器,应用服务器再把文件上传到OSS。具体流程如下图所示。
和数据直传到OSS相比,以上方法有三个缺点:
上传慢:用户数据需先上传到应用服务器,之后再上传到OSS。网络传输时间比直传到O
endpoint: http://192.168.1.55:9000
accessKey: 1777QN2GK9S6N02G83NK
secretKey: 6gMzG26973fXJfKxEOSxaUTrFdy3+QtSggdprESJ
bucketName: test
2. 创建minio的配置文件:
package com.common.properties;
import org.springframework.
直接在linux上进行环境搭建,使用命令进行下载minio并且启动服务
服务启动之后,可以直接对9000端口进行访问,同样的启动服务之后有日志进行输出。可以看到日志所表达的意思,在这里的一个控制台的端口是一个动态生成的,每一次启动服务的端口都会发生变换,在后续进行使用的使用肯定需要用到一个静态端口,可以使用来进行端口指定,并且第二个的话是指不推荐使用默认账号密码。
对账号密码进行设置,并且指定控制台端口进行启动。
在设置完账号密码之后进行启动,发现了报错,访问密钥长度至少3个字符,密钥长度至少8个字符……,
在Java中使用Minio进行对象存储时,上传一个对象后会返回一个临时URL,该URL在一定时间后会失效。如果需要获取永久的URL,可以使用Minio提供的`presignedPutObject`和`presignedGetObject`方法。
- `presignedGetObject`: 获取一个永久的可下载URL,该URL可以用于下载指定的对象。示例代码如下:
```java
// 初始化一个Minio客户端对象
MinioClient minioClient = new MinioClient("http://minio.example.com", "accessKey", "secretKey");
// 获取一个永久的可下载URL
String url = minioClient.presignedGetObject("my-bucket", "my-object", 60 * 60 * 24 * 7);
System.out.println("永久的可下载URL: " + url);
- `presignedPutObject`: 获取一个永久的可上传URL,该URL可以用于上传一个对象。示例代码如下:
```java
// 初始化一个Minio客户端对象
MinioClient minioClient = new MinioClient("http://minio.example.com", "accessKey", "secretKey");
// 获取一个永久的可上传URL
String url = minioClient.presignedPutObject("my-bucket", "my-object", 60 * 60 * 24 * 7);
System.out.println("永久的可上传URL: " + url);
以上示例代码中,`60 * 60 * 24 * 7`表示获取的URL有效期为一周,可以根据实际需求进行调整。获取到的永久URL可以保存在数据库或其他地方,用于后续的操作。