Minio概念
MinIO is an object storage solution that provides an Amazon Web Services S3-compatible API and supports all core S3 features. MinIO is built to deploy anywhere - public or private cloud, baremetal infrastructure, orchestrated environments, and edge infrastructure.
Minio搭建
在linux服务器上执行
mkdir -p /blessing/minio/data
mkdir -p /blessing/minio/config
docker run -dit \
--restart always \
-p 9000:9000 \
-p 9090:9090 \
--name minio \
-v /blessing/minio/data:/data \
-v /blessing/minio/config:/root/.minio \
-e "MINIO_ROOT_USER=DawnSilverGravel" \
-e "MINIO_ROOT_PASSWORD=DawnSilverGravel" \
quay.io/minio/minio:RELEASE.2023-06-02T23-17-26Z.fips server /data --console-address ":9090" --address ":9000"
使用docker启动参数具体在参考文档的第二个链接,此时RedHat Mminio镜像仓库没有最新的latest,故使用RELEASE.2023-06-02T23-17-26Z.fips
版本。
如果没有docker,同样在参考文档的第二链接,然后点击Linux标签页找到安装命令,在Minio版本可以找到对应的版本,以下是下载minio二进制的命令。
mkdir -p /blessing/minio/data
# 查看liunx系统架构,如果是x86_64就是AMD的
# 下载minio二进制文件
wget https://dl.min.io/server/minio/release/linux-amd64/minio -P /blessing/minio
# 赋予权限
cd /blessing/minio
chmod +x minio
# 启动minio --console-address控制台端口,默认是9001,默认上传端口是9000
# 账号密码默认:minioadmin/minioadmin
MINIO_ROOT_USER=DawnSilverGravel MINIO_ROOT_PASSWORD=DawnSilverGravel ./minio server /blessing/minio/data --console-address ":9090"
启动之后在浏览器访问http://yourAddress:9090 在Access Keys页面生成密钥得到access-key
和secret-key
。
Minio使用
Java API
在官方的文档中(下方参考文档第一个链接),以及MinioClient
类中,和下方参考文档的第三个链接中,都可以找到相关方法的示例,这些方法参数都有一个共同的特点,使用了Builder
模式,从中也使用了Consumer<T>
函数式接口,可以学习一下其思想。
bucket method 描述 bucketExists
检查bucket是否存在 listBuckets
获取所有bucket的信息 makeBucket
创建给定区域和对象锁特性的bucket removeBucket
删除一个无数据的bucket getBucketTags
获取bucket标签 setBucketTags
设置bucket标签,之前的tags会被删除 deleteBucketTags
删除bucket标签 getBucketVersioning
获取bucket版本 setBucketVersioning
设置bucket版本(版本默认是无,设置了之后就只能配置Enabled
和Suspended
) getObjectLockConfigurationArgs
获取bucket的对象锁配置,makeBucket
新建bucketobjectLock
参数为true
setObjectLockConfigurationArgs
设置bucket对象锁配置,makeBucket
新建bucketobjectLock
参数为true
,限制对对象的修改 deleteObjectLockConfigurationArgs
删除bucket对象锁配置makeBucket
新建bucketobjectLock
参数为true
...... 其余暂不了解
object method 描述 putObject
以流的形式上传对象 uploadObject
以文件的形式上传对象 copyObject
创建一个对象并复制minio服务器上的另一个对象的数据,两个对象不能相同 composeObject
组合minio服务器端不同的源对象来创建一个对象,每个对象大小不可以低于5M uploadSnowballObjects
在单个put中上传多个文件,SnowballObject
中size与指定的流大小要一致 removeObject
删除一个对象 removeObjects
删除多个对象 statObject
获取一个对象的对象信息和元数据 getObject
获取对象数据,并返回InputStream
流;,该流必须关闭以释放网络资源 getPresignedObjectUrl
获取一个对象的http方法、过期时间、自定义参数的URL,GetPresignedObjectUrlArgs
有GET
、PUT
、HEAD
三种类型的http方法 downloadObject
以本地文件的形式下载一个对象,DownloadObjectArgs
中的fileName
中文件名如果本地存在将会下载失败 getPresignedPostFormData
获取一个对象PostPolicy的表单数据的POST
方法URL,然后使用该URL上传文件 listObjects
获取一个bucket里对象列表 getObjectTags
获取对象的标签 setObjectTags
设置对象标签 deleteObjectTags
删除对象标签 getObjectRetention
获取对象保留策略配置,指定创建bucket创建时需要设置objectLock
为true
setObjectRetention
设置对象保留策略配置,指定创建bucket创建时需要设置objectLock
为true
enableObjectLegalHold
启用对对象的合法保留,默认为false
,上传对象时可设置,指定bucket创建时要设置objectLock
为true
disableObjectLegalHold
禁用对对象的合法保留,指定bucket创建时设置objectLock
为true
isObjectLegalHoldEnabled
返回对象是否启用合法保留,启用为true
,指定bucket创建时要设置objectLock
为true
selectObjectContent
通过SQL表达式选择对象的内容(不知如何使用) restoreObject
归档一个对象(不知如何使用)
以PutObjectArgs
为例:
minio没有文件夹的概念,但是可以使用object(/blessing/yourFileName)
可以创建一个blessing文件夹
针对于putObject
方法中的参数对象PutObjectArgs
中的stream
方法有其中两个参数objectSize
与partSize
,有以下描述:
允许最大对象为5TB
允许最小的分片为5M
允许最大分片为5GB(这里注释看了一下代码应该是写错了,版本8.5.3
)
最大的分片数量为10000
从图中可以得知这是一个父类,则下图中的子类同样有上述规则。
项目结构与配置
pom.xml
<dependencies>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.3</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.15</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.12</version>
</dependency>
</dependencies>
application.yml
minio:
init-buckets:
- dawn-silver-gravel
- blessing-star
operation-bucket: dawn-star
access-key: yourAccessKey
secret-key: yourSecretKey
# 默认端口9000
endpoint: http://yourAddress:yourPort
config配置
package com.example.config;
import io.minio.MinioClient;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
* description:
* @author DawnStar
* date: 2023/6/14
@Configuration
@EnableConfigurationProperties(MinioProperties.class)
public class MinioConfiguration {
@Resource
private MinioProperties minioProperties;
@Bean
public MinioClient minioClient() {
return MinioClient.builder().credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey())
.endpoint(minioProperties.getEndpoint())
.build();
package com.example.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
* description:
* @author DawnStar
* date: 2023/6/14
@ConfigurationProperties(prefix = "minio")
@Component
public class MinioProperties {
private String accessKey;
private String secretKey;
private String endpoint;
private String[] initBuckets;
private String operationBucket;
public String getOperationBucket() {
return operationBucket;
public void setOperationBucket(String operationBucket) {
this.operationBucket = operationBucket;
public String[] getInitBuckets() {
return initBuckets;
public void setInitBuckets(String[] initBuckets) {
this.initBuckets = initBuckets;
public String getAccessKey() {
return accessKey;
public void setAccessKey(String accessKey) {
this.accessKey = accessKey;
public String getSecretKey() {
return secretKey;
public void setSecretKey(String secretKey) {
this.secretKey = secretKey;
public String getEndpoint() {
return endpoint;
public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
文件工具类
package com.example.util;
import java.io.*;
import java.nio.file.Files;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
* description:
* 生成文件工具类
* @author DawnStar
* date: 2023/6/15
public class MinioFileUtils {
private final static ThreadLocalRandom RANDOM = ThreadLocalRandom.current();
public static InputStream createFile(String fileName) {
File file = new File(fileName);
try (BufferedWriter bufferedWriter = new BufferedWriter
(
new OutputStreamWriter(Files.newOutputStream(file.toPath())))) {
bufferedWriter.write("这是一个测试文件");
return Files.newInputStream(file.toPath());
} catch (IOException e) {
throw new RuntimeException(e);
public static File getUploadFile(String fileName) {
File file = new File(fileName);
try (BufferedWriter bufferedWriter = new BufferedWriter(
new OutputStreamWriter(Files.newOutputStream(file.toPath())))) {
bufferedWriter.write(generateContent(fileName));
return file;
} catch (IOException e) {
throw new RuntimeException(e);
public static String generateContent(String fileName) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(fileName).append("\n");
int anInt = RANDOM.nextInt(300, 500);
int lineLength = 50;
for (int i = 0; i < anInt; i++) {
for (int i1 = 0; i1 < lineLength; i1++) {
char c = (char) RANDOM.nextInt(30, 122);
stringBuilder.append(c);
stringBuilder.append("\n");
return stringBuilder.toString();
public static String generateContentPartSize(String fileName,long partSize) {
if (partSize < 5 * 1024 * 1024) {
throw new IllegalArgumentException("非法参数");
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(fileName).append("\n");
int lineLength = 50;
long l = partSize / 50 + 1;
for (long i = 0; i < l; i++) {
for (int i1 = 0; i1 < lineLength; i1++) {
char c = (char) RANDOM.nextInt(30, 122);
stringBuilder.append(c);
stringBuilder.append("\n");
return stringBuilder.toString();
Minio 部分Java API 使用
MinioBucketApi
package com.example.template;
import io.minio.*;
import io.minio.messages.*;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
* description:
* minio 方法测试
* @author DawnStar
* date: 2023/6/15
@Component
public class MinioBucketApi {
@Resource
private MinioClient minioClient;
@Value("${minio.operation-bucket}")
private String buckName;
* 桶标签操作
@SneakyThrows
public void bucketTagsOperation() {
System.out.println("***********************桶标签操作*******************************");
Tags tags = Tags.newBucketTags(new HashMap<String, String>(2) {{
this.put("blessing", "star");
this.put("images", "image");
SetBucketTagsArgs setBucketTagsArgs = SetBucketTagsArgs.builder().bucket(buckName).tags(tags).build();
minioClient.setBucketTags(setBucketTagsArgs);
System.out.println("================获取桶标签========================");
GetBucketTagsArgs getBucketTagsArgs = GetBucketTagsArgs.builder().bucket(buckName).build();
Tags bucketTags = minioClient.getBucketTags(getBucketTagsArgs);
System.out.println(buckName + "标签:" + bucketTags.get());
System.out.println("================设置桶标签========================");
SetBucketTagsArgs newSetBucketTagsArgs = SetBucketTagsArgs.builder().bucket(buckName).tags(new HashMap<String, String>(1) {{
this.put("newTag", "newTag");
}}).build();
minioClient.setBucketTags(newSetBucketTagsArgs);
Tags bucketTags1 = minioClient.getBucketTags(getBucketTagsArgs);
System.out.println(buckName + "标签:" + bucketTags1.get());
System.out.println("================删除桶标签=======================");
DeleteBucketTagsArgs deleteBucketTagsArgs = DeleteBucketTagsArgs.builder().bucket(buckName).build();
minioClient.deleteBucketTags(deleteBucketTagsArgs);
Tags newTags = minioClient.getBucketTags(getBucketTagsArgs);
System.out.println(buckName + "标签:" + newTags.get());
System.out.println("***********************************************************************************");
* 桶版本操作
@SneakyThrows
public void bucketVersionOperation() {
System.out.println("***********************桶版本操作*******************************");
System.err.println("桶版本默认没有,开启了就只有两种状态:Enable 和 Suspended状态");
System.out.println("=================获取桶版本===================");
GetBucketVersioningArgs getBucketVersioningArgs = GetBucketVersioningArgs.builder().bucket(buckName).build();
VersioningConfiguration bucketVersioning = minioClient.getBucketVersioning(getBucketVersioningArgs);
System.out.println(buckName + "版本:" + bucketVersioning.status().toString() + bucketVersioning.isMfaDeleteEnabled());
System.out.println("===================设置桶版本==================");
VersioningConfiguration.Status enabled = VersioningConfiguration.Status.ENABLED;
VersioningConfiguration configuration = new VersioningConfiguration(enabled, true);
SetBucketVersioningArgs setBucketVersioningArgs = SetBucketVersioningArgs.builder().bucket(buckName).config(configuration).build();
minioClient.setBucketVersioning(setBucketVersioningArgs);
bucketVersioning = minioClient.getBucketVersioning(getBucketVersioningArgs);
System.out.println(buckName + "版本:" + bucketVersioning.status().toString() + bucketVersioning.isMfaDeleteEnabled());
System.out.println("***********************************************************************************");
* 桶基本操作
@SneakyThrows
public void bucketBasicOperation() {
System.out.println("***********************桶基本操作*******************************");
System.out.println("===============检查桶是否存在========================");
BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder().bucket(buckName).build();
boolean bucketExists = minioClient.bucketExists(bucketExistsArgs);
System.out.println(buckName + "存在:" + bucketExists);
// 获取桶列表
System.out.println("=================获取桶列表======================");
List<Bucket> buckets = minioClient.listBuckets();
buckets.forEach(bucket -> System.out.println(bucket.name()));
System.out.println("=================新建桶============================");
MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder().bucket("test").objectLock(true).build();
// 创建已有bucket会创建失败
minioClient.makeBucket(makeBucketArgs);
buckets = minioClient.listBuckets();
buckets.forEach(bucket -> System.out.println(bucket.name()));
// 删除桶,有文件就会删除失败
System.out.println("=================删除桶======================");
RemoveBucketArgs removeBucketArgs = RemoveBucketArgs.builder().bucket("test").build();
minioClient.removeBucket(removeBucketArgs);
buckets = minioClient.listBuckets();
buckets.forEach(bucket -> System.out.println(bucket.name()));
System.out.println("***********************************************************************************");
@SneakyThrows
public void objectLockConfigurationOperation() {
System.out.println("***********************桶对象锁配置*******************************");
GetObjectLockConfigurationArgs getObjectLockConfigurationArgs = GetObjectLockConfigurationArgs.builder().bucket(buckName).build();
ObjectLockConfiguration objectLockConfiguration = minioClient.getObjectLockConfiguration(getObjectLockConfigurationArgs);
System.out.println("获取当前的bucket对象锁配置"+objectLockConfiguration.mode() + " " + objectLockConfiguration.duration());
objectLockConfiguration = new ObjectLockConfiguration(RetentionMode.COMPLIANCE, new RetentionDurationDays(1));
minioClient.setObjectLockConfiguration(SetObjectLockConfigurationArgs.builder().bucket(buckName).config(objectLockConfiguration).build());
objectLockConfiguration = minioClient.getObjectLockConfiguration(getObjectLockConfigurationArgs);
System.out.println("设置后的bucket对象锁配置"+objectLockConfiguration.mode() + " " + objectLockConfiguration.duration());
minioClient.deleteObjectLockConfiguration(DeleteObjectLockConfigurationArgs.builder().bucket(buckName).build());
objectLockConfiguration = minioClient.getObjectLockConfiguration(getObjectLockConfigurationArgs);
System.out.println("删除桶锁对象配置:"+objectLockConfiguration.mode() + " " + objectLockConfiguration.duration());
System.out.println("***********************************************************************************");
MinioObjectApi
package com.example.template;
import cn.hutool.http.ContentType;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.example.util.MinioFileUtils;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.*;
import lombok.SneakyThrows;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import
java.io.*;
import java.nio.charset.StandardCharsets;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
* description:
* @author DawnStar
* date: 2023/6/16
@Component
public class MinioObjectApi {
@Resource
private MinioClient minioClient;
@Value("${minio.operation-bucket}")
private String buckName;
@SneakyThrows
public void putObject() {
// 上传已知的大小的文件
System.out.println("************************************putObject操作***********************************************");
String fileName = "test.txt";
InputStream inputStream = MinioFileUtils.createFile(fileName);
System.out.println("==================上传已知的大小的文件==========================");
PutObjectArgs firstObjectArgs = PutObjectArgs.builder()
.bucket(buckName)
.object(fileName)
// -1 表示默认分片为 5M
.stream(inputStream, inputStream.available(), -1).build();
ObjectWriteResponse objectWriteResponse = minioClient.putObject(firstObjectArgs);
System.out.println(objectWriteResponse.versionId() + " " + objectWriteResponse.etag());
// 上传未知大小的文件
System.out.println("==================上传未知的大小的文件==========================");
PutObjectArgs secondObjectArgs = PutObjectArgs.builder()
.bucket(buckName)
.object(fileName)
.stream(inputStream, -1, PutObjectArgs.MIN_MULTIPART_SIZE)
.build();
objectWriteResponse = minioClient.putObject(secondObjectArgs);
System.out.println(objectWriteResponse.versionId() + " " + objectWriteResponse.etag());
// 上传未知大小的文件
System.out.println("==================上传内存中的数据==========================");
String content = "这是一个内存运行的数据";
Map<String, String> headers = new HashMap<>();
headers.put("X-Amz-Storage-Class", "REDUCED_REDUNDANCY");
Map<String, String> userMetadata = new HashMap<>();
byte[] contentBytes = content.getBytes(StandardCharsets.UTF_8);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(contentBytes);
PutObjectArgs thirdObjectArgs = PutObjectArgs.builder()
.bucket(buckName)
// 将创建一个名为putMethod文件夹
.object("putMethod/content.txt")
// 两者不能同时为-1
.stream(byteArrayInputStream, -1, PutObjectArgs.MIN_MULTIPART_SIZE)
.headers(headers)
.userMetadata(userMetadata)
.build();
objectWriteResponse = minioClient.putObject(thirdObjectArgs);
System.out.println(objectWriteResponse.versionId() + " " + objectWriteResponse.etag());
System.out.println("***********************************************************************************");
@SneakyThrows
public void uploadObject() {
System.out.println("************************************uploadObject操作***********************************************");
String fileName = "uploadObject.txt";
File file = MinioFileUtils.getUploadFile(fileName);
UploadObjectArgs uploadObjectArgs = UploadObjectArgs.builder()
.bucket(buckName)
.object(fileName)
// .filename(file.toPath())
.filename(file.toPath().toString(), UploadObjectArgs.MAX_PART_SIZE)
.build();
ObjectWriteResponse objectWriteResponse = minioClient.uploadObject(uploadObjectArgs);
System.out.println(objectWriteResponse.versionId() + " " + objectWriteResponse.etag());
System.out.println("***********************************************************************************");
@SneakyThrows
public void copyObject() {
String fileName = "copy.txt";
putObjectByPartSize("", fileName);
System.out.println("************************************copyObject操作***********************************************");
// object() 与 source()不可相同
CopyObjectArgs copyObjectArgs = CopyObjectArgs.builder()
.bucket(buckName)
.object("copyMethod/" + fileName)
.source(CopySource.builder().bucket(buckName).object(fileName).build())
.build();
ObjectWriteResponse objectWriteResponse = minioClient.copyObject(copyObjectArgs);
System.out.println(objectWriteResponse.versionId() + " " + objectWriteResponse.etag());
System.out.println("***********************************************************************************");
@SneakyThrows
public void composeObject() {
System.out.println("************************************composeObject操作***********************************************");
List<ComposeSource> composeSources = new ArrayList<>();
String prefix = "subComposeObject";
for (int i = 0; i < 2; i++) {
String composeFileName = prefix + i + ".txt";
putObjectByPartSize("composeMethod", composeFileName);
ComposeSource composeSource = ComposeSource.builder()
.bucket(buckName)
.object("composeMethod/" + composeFileName)
.build();
composeSources.add(composeSource);
String fileName = "composeObject.txt";
ComposeObjectArgs composeObjectArgs = ComposeObjectArgs.builder()
.bucket(buckName)
.object("composeMethod/" + fileName)
// 每个文件大小不可以低于5M
.sources(composeSources)
.build();
ObjectWriteResponse objectWriteResponse = minioClient.composeObject(composeObjectArgs);
System.out.println(objectWriteResponse.versionId() + " " + objectWriteResponse.etag());
System.out.println("***********************************************************************************");
@SneakyThrows
public void uploadSnowballObjects() {
System.out.println("************************************uploadSnowballObjects操作***********************************************");
String prefix = "uploadSnowballMethod";
List<SnowballObject> objects = new ArrayList<>();
// 参数中size必须与文件大小相等
String content = MinioFileUtils.generateContent(prefix + "my-object-one");
byte[] contentBytes = content.getBytes(StandardCharsets.UTF_8);
InputStream inputStream = MinioFileUtils.createFile(prefix + "my-object-two");
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(contentBytes);
objects.add(
new SnowballObject(
prefix + "/my-object-one",
byteArrayInputStream,
byteArrayInputStream.available(),
null));
objects.add(
new SnowballObject(
prefix + "/my-object-two",
inputStream,
inputStream.available(),
null));
ObjectWriteResponse objectWriteResponse = minioClient.uploadSnowballObjects(
UploadSnowballObjectsArgs.builder().bucket(buckName).objects(objects).build());
System.out.println(objectWriteResponse.versionId() + " " + objectWriteResponse.etag());
System.out.println("***********************************************************************************");
@SneakyThrows
public void removeObject() {
System.out.println("************************************removeObject与removeObjects操作***********************************************");
System.out.println("====================删除单个对象================================");
RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder().bucket(buckName).object("test").build();
// 删除不存在的对象也不会有问题
minioClient.removeObject(removeObjectArgs);
DeleteObject deleteObject = new DeleteObject("test");
System.out.println("====================删除多个对象================================");
String prefix = "removeMethod";
DeleteObject deleteObject1 = new DeleteObject(prefix + "/removeObject.txt");
putObjectByPartSize(prefix, "removeObject.txt");
RemoveObjectsArgs removeObjectsArgs = RemoveObjectsArgs.builder().bucket(buckName).objects(new ArrayList<>() {{
this.add(deleteObject);
this.add(deleteObject1);
}}).build();
Iterable<Result<DeleteError>> results = minioClient.removeObjects(removeObjectsArgs);
// 删除不存在的对象也不会有问题
for (Result<DeleteError> deleteErrorResult : results) {
System.out.println(deleteErrorResult.get().toString());
System.out.println("***********************************************************************************");
@SneakyThrows
public void statObject() {
System.out.println("************************************statObjects操作***********************************************");
uploadObject("statMethod", "statObject.txt");
StatObjectArgs statObjectArgs = StatObjectArgs.builder()
.bucket(buckName)
.object("statMethod/statObject.txt")
.build();
StatObjectResponse statObjectResponse = minioClient.statObject(statObjectArgs);
System.out.println("statObject:" + statObjectResponse.etag() + " " + statObjectResponse.object() + " " + statObjectResponse.versionId());
System.out.println("***********************************************************************************");
@SneakyThrows
public void getObject() {
System.out.println("************************************getObjects操作***********************************************"
);
uploadObject("getMethod", "getObject.txt");
GetObjectArgs getObjectArgs = GetObjectArgs.builder()
.bucket(buckName)
.object("getMethod/getObject.txt")
// 起始位置
.offset(10L)
// 数据长度
.length(100L)
.build();
GetObjectResponse response = minioClient.getObject(getObjectArgs);
byte[] bytes = response.readAllBytes();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(bytes.length);
System.out.println(byteArrayOutputStream);
byteArrayOutputStream.close();
response.close();
System.out.println("***********************************************************************************");
@SneakyThrows
public void getPresignedObjectUrl() {
System.out.println("************************************getPresignedObjectUrl操作***********************************************");
GetPresignedObjectUrlArgs getPresignedObjectUrlArgs = GetPresignedObjectUrlArgs.builder()
.bucket(buckName)
.object("getPresignedMethod/notGetPresignedObjectUrl.txt")
.expiry(24, TimeUnit.HOURS)
.method(Method.GET)
.build();
String presignedObjectUrl = minioClient.getPresignedObjectUrl(getPresignedObjectUrlArgs);
System.out.println("没有该文件的路径" + presignedObjectUrl);
// 获取上传路径
GetPresignedObjectUrlArgs putPresignedObjectUrlArgs = GetPresignedObjectUrlArgs.builder()
.bucket(buckName)
.object("getPresignedMethod/getPresignedObjectUrl.txt")
.expiry(24, TimeUnit.HOURS)
.method(Method.PUT)
.build();
presignedObjectUrl = minioClient.getPresignedObjectUrl(putPresignedObjectUrlArgs);
System.out.println("PUT方法上传路径:" + presignedObjectUrl);
// 上传数据
HttpRequest request = HttpUtil.createRequest(cn.hutool.http.Method.PUT, presignedObjectUrl);
byte[] bytes = MinioFileUtils.generateContent("getPresignedObjectUrl.txt").getBytes(StandardCharsets.UTF_8);
HttpResponse execute = request.body(bytes).contentType(ContentType.TEXT_PLAIN.getValue()).execute();
execute.close();
GetPresignedObjectUrlArgs targetPresignedObjectUrlArgs = GetPresignedObjectUrlArgs.builder()
.bucket(buckName)
.object("getPresignedMethod/getPresignedObjectUrl.txt")
.expiry(24, TimeUnit.HOURS)
.method(Method.GET)
.build();
presignedObjectUrl = minioClient.getPresignedObjectUrl(targetPresignedObjectUrlArgs);
System.out.println("获取路径:" + presignedObjectUrl);
GetPresignedObjectUrlArgs headPresignedObjectUrlArgs = GetPresignedObjectUrlArgs.builder()
.bucket(buckName)
.object("getPresignedMethod/getPresignedObjectUrl.txt")
.method(Method.HEAD)
.build();
presignedObjectUrl = minioClient.getPresignedObjectUrl(headPresignedObjectUrlArgs);
System.out.println("HEAD方法路径,请通过postman请求该路径:" + presignedObjectUrl);
System.out.println("***********************************************************************************");
@SneakyThrows
public void downLoadObject() {
System.out.println("************************************downLoadObject操作***********************************************");
uploadObject("downloadMethod", "downloadObject.txt");
DownloadObjectArgs downloadObjectArgs = DownloadObjectArgs.builder()
.bucket(buckName)
.object("downloadMethod/downloadObject.txt")
.filename("downloadObject.txt")
.build();
// 文件名存在将会下载失败
System.err.println("文件名存在将会下载失败");
minioClient.downloadObject(downloadObjectArgs);
File file = new File("downloadObject.txt");
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
String readLine;
while ((readLine = bufferedReader.readLine()) != null) {
System.out.print(readLine);
bufferedReader.close();
System.out.println("***********************************************************************************");
@Value("${minio.endpoint}")
private String url;
@SneakyThrows
public void getPresignedPostFormData() {
System.out.println("************************************getPresignedPostFormData操作***********************************************");
// 创建一个时效为7天的策略
PostPolicy policy = new PostPolicy(buckName, ZonedDateTime.now().plusDays(7));
String objectName = "PostFormMethod/postFormData.txt";
// 添加条件,`key`键为对象名称
policy.addEqualsCondition("key", objectName);
// 添加'Content-Type.设置前缀为“application”
policy.addStartsWithCondition("Content-Type", "application/");
// 添加内容长度范围,20Bit - 1MiB,不在这个区间的文件上传失败
policy.addContentLengthRangeCondition(20, 1024*1024);
Map<String, String> formData = minioClient.getPresignedPostFormData(policy);
// 以表单的形式上传文本
MultipartBody.Builder multipartBuilder = new MultipartBody.Builder();
multipartBuilder.setType(MultipartBody.FORM);
for (Map.Entry<String, String> entry : formData.entrySet()) {
multipartBuilder.addFormDataPart(entry.getKey(), entry.getValue());
multipartBuilder.addFormDataPart("key", objectName);
multipartBuilder.addFormDataPart("Content-Type",ContentType.OCTET_STREAM.getValue());
String fileName = objectName.substring(objectName.lastIndexOf("/") + 1);
File uploadFile = MinioFileUtils.getUploadFile(fileName);
// "file" 必须最后添加
multipartBuilder.addFormDataPart(
"file", objectName, RequestBody.create(uploadFile, null));
System.out.println(url+"/"+objectName);
Request request =
new Request.Builder()
// minio服务器路径
.url(url +"/"+buckName)
.post(multipartBuilder.build())
.build();
OkHttpClient httpClient = new OkHttpClient().newBuilder().build();
try (Response response = httpClient.newCall(request).execute();) {
if (response.isSuccessful()) {
System.out.println(fileName + " is uploaded successfully using POST object");
} else {
System.out.println("Failed to upload " + fileName);
System.out.println("***********************************************************************************");
@SneakyThrows
public void listObjects() {
System.out.println("************************************listObjects操作***********************************************");
uploadObject("listMethod", "listObject1.txt");
uploadObject("listMethod", "listObject2.txt");
uploadObject("listMethod", "listObject3.txt");
System.out.println("===================第一种List==================");
ListObjectsArgs listObjectsArgs = ListObjectsArgs.builder().bucket(buckName).build();
Iterable<Result<Item>> listObjects = minioClient.listObjects(listObjectsArgs);
print(listObjects);
System.out.println("===================第二种List==================");
listObjectsArgs = ListObjectsArgs.builder()
.bucket(buckName)
.recursive(true)
.build();
print(minioClient.listObjects(listObjectsArgs));
System.out.println("===================第三种List==================");
listObjectsArgs = ListObjectsArgs.builder()
.bucket(buckName)
.recursive(true)
.prefix("listMethod")
.build();
print(minioClient.listObjects(listObjectsArgs));
System.out.println("===================第四种List==================");
listObjectsArgs = ListObjectsArgs.builder()
.bucket(buckName)
.recursive(true)
.prefix("listMethod")
.startAfter("listMethod/listObject2.txt")
.build();
print(minioClient.listObjects(listObjectsArgs));
// ......
System.out.println("***********************************************************************************");
private void print(Iterable<Result<Item>> listObjects) throws Exception {
for (Result<Item> listObject : listObjects) {
System.out.println(listObject.get().objectName());
@SneakyThrows
public void objectRetentionOperation() {
System.out.println("************************************对象保留策略操作***********************************************");
System.err.println("创建bucket需要设置objectLock为true");
uploadObject("retentionMethod", "objectRetention.txt");
GetObjectRetentionArgs getObjectRetentionArgs = GetObjectRetentionArgs.builder().bucket(buckName).object("retentionMethod/objectRetention.txt")
.build();
Retention objectRetention = minioClient.getObjectRetention(getObjectRetentionArgs);
if (objectRetention == null) {
System.out.println("修改策略前配置: null");
} else {
System.out.println("修改策略前配置: " + objectRetention.mode().toString() + " " + objectRetention.retainUntilDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
Retention retention = new Retention(RetentionMode.COMPLIANCE, ZonedDateTime.now().plusSeconds(40));
SetObjectRetentionArgs setObjectRetentionArgs = SetObjectRetentionArgs.builder()
.bucket(buckName)
.object("retentionMethod/objectRetention.txt")
.config(retention)
// ByPass Mode(略过模式或旁路模式),泛指在一个系统的正常流程中,
// 有一堆检核机制,而“ByPass Mode”就是当检核机制发生异常,
// 无法在短期间内排除时,使系统作业能绕过这些检核机制,
// 使系统能够继续运行的作业模式
.bypassGovernanceMode(true)
.build();
minioClient.setObjectRetention(setObjectRetentionArgs);
objectRetention = minioClient.getObjectRetention(getObjectRetentionArgs);
System.out.println("修改策略后配置:" + objectRetention.mode().toString() + " " + objectRetention.retainUntilDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
System.out.println("***********************************************************************************");
@SneakyThrows
public void objectLegalHoldOperation
() {
System.out.println("************************************对象合法保留操作***********************************************");
System.err.println("创建bucket需要设置objectLock为true");
uploadObject("legalHoldMethod", "objectLegalHoldMethod.txt");
IsObjectLegalHoldEnabledArgs isObjectLegalHoldEnabledArgs = IsObjectLegalHoldEnabledArgs.builder()
.bucket(buckName)
.object("legalHoldMethod/objectLegalHoldMethod.txt")
.build();
boolean objectLegalHoldEnabled = minioClient.isObjectLegalHoldEnabled(isObjectLegalHoldEnabledArgs);
System.out.println("执行isObjectLegalHoldEnabledArgs:" + objectLegalHoldEnabled);
EnableObjectLegalHoldArgs enableObjectLegalHoldArgs = EnableObjectLegalHoldArgs.builder()
.bucket(buckName)
.object("legalHoldMethod/objectLegalHoldMethod.txt")
.build();
minioClient.enableObjectLegalHold(enableObjectLegalHoldArgs);
objectLegalHoldEnabled = minioClient.isObjectLegalHoldEnabled(isObjectLegalHoldEnabledArgs);
System.out.println("执行enableObjectLegalHold:" + objectLegalHoldEnabled);
DisableObjectLegalHoldArgs disableObjectLegalHoldArgs = DisableObjectLegalHoldArgs.builder()
.bucket(buckName)
.object("legalHoldMethod/objectLegalHoldMethod.txt")
.build();
minioClient.disableObjectLegalHold(disableObjectLegalHoldArgs);
objectLegalHoldEnabled = minioClient.isObjectLegalHoldEnabled(isObjectLegalHoldEnabledArgs);
System.out.println("执行disableObjectLegalHold:" + objectLegalHoldEnabled);
System.out.println("***********************************************************************************");
@SneakyThrows
public void objectTagOperation() {
System.out.println("************************************对象标签操作***********************************************");
uploadObject("tagMethod", "objectTags.txt");
// 与给bucket添加标签的方法是相似的
SetObjectTagsArgs setObjectTagsArgs = SetObjectTagsArgs.builder()
.bucket(buckName)
.object("tagMethod/objectTags.txt")
.tags(new HashMap<String, String>(1) {{
this.put("blessing", "star");
.build();
minioClient.setObjectTags(setObjectTagsArgs);
GetObjectTagsArgs getObjectTagsArgs = GetObjectTagsArgs.builder()
.bucket(buckName)
.object("tagMethod/objectTags.txt")
.build();
Tags objectTags = minioClient.getObjectTags(getObjectTagsArgs);
System.out.println("设置后的标签:" + objectTags.get());
DeleteObjectTagsArgs deleteObjectTagsArgs = DeleteObjectTagsArgs.builder()
.bucket(buckName)
.object("tagMethod/objectTags.txt")
.build();
minioClient.deleteObjectTags(deleteObjectTagsArgs);
objectTags = minioClient.getObjectTags(getObjectTagsArgs);
System.out.println("删除后的标签对象:" + objectTags.get());
System.out.println("***********************************************************************************");
// @SneakyThrows
// public void selectObjectContent() {
// uploadObject("selectMethod", "selectObjectContent1.txt");
// uploadObject("selectMethod", "selectObjectContent2.txt");
// uploadObject("selectMethod", "selectObjectContent3.txt");
// String sqlExpression = "select * from S3Object";
// InputSerialization is = new InputSerialization(null, false, null, null, FileHeaderInfo.USE, null, null, null);
// OutputSerialization os = new OutputSerialization(null, null, null, QuoteFields.ASNEEDED, null);
// SelectResponseStream stream =
// minioClient.selectObjectContent(
// SelectObjectContentArgs.builder()
// .bucket(buckName)
// .object("selectMethod/selectObjectContent3.txt")
// .sqlExpression(sqlExpression)
// .inputSerialization(is)
// .outputSerialization(os)
// .requestProgress(true)
// .build());
// byte[] buf = new byte[512];
// int bytesRead = stream.read(buf, 0, buf.length);
// System.out.println(new String(buf, 0, bytesRead, StandardCharsets.UTF_8));
// Stats stats = stream.stats();
// System.out.println("bytes scanned: " + stats.bytesScanned());
// System.out.println("bytes processed: " + stats.bytesProcessed());
// System.out.println("bytes returned: " + stats.bytesReturned());
// stream.close();
// }
/// The operation is not valid for the current state of the object.不知道还需要什么状态
// @SneakyThrows
// public void restoreObject() {
// System.out.println("************************************restoreObject操作***********************************************");
// uploadObject("restoreMethod", "restoreObject.txt");
//// minioClient.removeObject(RemoveObjectArgs.builder().bucket(buckName).object("restoreMethod/restoreObject.txt").build());
// RestoreObjectArgs restoreObjectArgs = RestoreObjectArgs.builder()
// .bucket(buckName)
// .object("restoreMethod/restoreObject.txt")
// .request(new RestoreRequest(null, null, null, null, null, null))
// .build();
// minioClient.restoreObject(restoreObjectArgs);
// System.out.println("***********************************************************************************");
// }
@SneakyThrows
public void uploadObject(String folder, String fileName) {
byte[] contentBytes = MinioFileUtils.generateContent(fileName).getBytes(StandardCharsets.UTF_8);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(contentBytes);
PutObjectArgs thirdObjectArgs = PutObjectArgs.builder()
.bucket(buckName)
// 将创建一个名为putMethod文件夹
.object(folder + "/" + fileName)
// 两者不能同时为-1
.stream(byteArrayInputStream, -1, PutObjectArgs.MIN_MULTIPART_SIZE)
.build();
ObjectWriteResponse objectWriteResponse = minioClient.putObject(thirdObjectArgs);
@SneakyThrows
private void putObjectByPartSize(String folder, String fileName) {
String content = MinioFileUtils.generateContentPartSize(fileName, PutObjectArgs.MIN_MULTIPART_SIZE);
byte[] contentBytes = content.getBytes(StandardCharsets.UTF_8);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(contentBytes);
PutObjectArgs thirdObjectArgs = PutObjectArgs.builder()
.bucket(buckName)
// 将创建一个名为putMethod文件夹
.object(folder + "/" + fileName)
// 两者不能同时为-1
.stream(byteArrayInputStream, -1, PutObjectArgs.MIN_MULTIPART_SIZE)
.build();
minioClient.putObject(thirdObjectArgs);
package com.example;
import io.minio.*;
import io.minio.messages.*;
import com.example.config.MinioProperties;
import com.example.template.MinioBucketApi;
import com.example.template.MinioObjectApi;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
* Description:
* @author DawnStar
* Date: 2023/6/16 19:24
@SpringBootApplication
public class MinioApplication implements ApplicationRunner {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = new SpringApplicationBuilder(MinioApplication.class).web(WebApplicationType.NONE).run(args);
MinioBucketApi bean = context.getBean(MinioBucketApi.class);
invokeMethod(bean);
// bean.objectLockConfigurationOperation();
MinioObjectApi objectApiBean = context.getBean(MinioObjectApi.class);
objectApiBean.getPresignedPostFormData();
invokeMethod(objectApiBean);
System.out.println("结束");
// objectApiBean.objectLegalHoldOperation();
// SpringApplication.run(MinioApplication.class, args);
@Resource
private MinioProperties minioProperties;
@Override
public void run(ApplicationArguments args) throws Exception {
if (minioProperties.getInitBuckets().length == 0) {
return;
// 初始化桶
MinioClient minioClient = MinioClient.builder().credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey())
.endpoint(minioProperties.getEndpoint())
.build();
// 获取所有桶
List<Bucket> buckets = minioClient.listBuckets();
List<String> collect = buckets.stream().map(Bucket::name).collect(Collectors.toList());
List<String> targetBucketNames = Arrays.stream(minioProperties.getInitBuckets()).filter(s -> !collect.contains(s)).collect(Collectors.toList());
for (String targetBucketName : targetBucketNames) {
MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder().bucket(targetBucketName).build();
minioClient.makeBucket(makeBucketArgs);
String operationBucket = minioProperties.getOperationBucket();
boolean bucketExists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(operationBucket).build());
if (!bucketExists) {
// 只能在创建时候才能设置对象锁,之后都无效
minioClient.makeBucket(MakeBucketArgs.builder().bucket(operationBucket).objectLock(true).build());
// 防止bucket被设置了对象锁,无法修改minio服务器的对象
minioClient.deleteObjectLockConfiguration(DeleteObjectLockConfigurationArgs.builder().bucket(operationBucket).build());
private static void invokeMethod(Object o) {
Method[] declaredMethods = o.getClass().getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
if (declaredMethod.getParameters().length > 0) {
continue;
try {
if (declaredMethod.getReturnType().equals(void.class) && !declaredMethod.getName().contains("lambda")) {
declaredMethod.invoke(o);
} catch (Exception e) {
e.printStackTrace();
System.out.println("\n\n");
关于Nginx代理问题
通过nginx代理,https下,通过getPresignedObjectUrl
方法获取文件URL可能无法访问指定的文件。
以下是官方给出的一种方案,详情请访问Minio Nginx代理
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 300;
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
proxy_http_version 1.1;
proxy_set_header Connection "";
chunked_transfer_encoding off;
proxy_pass https://minio:9000/; # This uses the upstream directive definition to load balance
location /minio {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-NginX-Proxy true;
# This is necessary to pass the correct IP to be hashed
real_ip_header X-Real-IP;
proxy_connect_timeout 300;
# To support websockets in MinIO versions released after January 2023
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
chunked_transfer_encoding off;
proxy_pass https://minio:9001/; # This uses the upstream directive definition to load balance and assumes a static Console port of 9001
关于秒传,断点续传,分片续传问题
目前(2023/06/17) Minio Java API没有提供断点续传功能,所以如果想实现上述功能,可以借助Redis,和继承MinioAsyncClient
类并将其重写开放部分方法的访问权限来实现相关功能。可以参考这篇博客blog.csdn.net/Alan_ran/ar… 不过他的版本是低的,所以有些方法过时了如
listParts
方法便是过时的。
当然官方API也给予相关的方法是listPartsAsync
方法,可以看出listParts
方法中也是调用了listPartsAsync
方法。更多具体细节请查看S3Base
类以及其子类MinioAsyncClient
,在8.5.3
中MinioClinet
其实是作为一个工具类,其内部封装了MinioAsyncClient
类。