对象存储服务minio

我的第一印象

去年7月份的大数据项目中使用的它做图片服务器,当时也就只听了个名字。这次需要涉及到视频处理和上传又用到了,所以来补一下知识点。

简单、好用、方便快捷。

minio服务器

MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。

MinIO是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或者 MySQL。(来自官方文档)

亚马逊S3云存储服务

第一次看到这个词,怀着好奇心到amazon官网了解了一下,原介绍如下:

专为从任意位置存储和检索任意数量的数据而构建的对象存储

Amazon Simple Storage Service (Amazon S3) 是一种对象存储服务,提供行业领先的可扩展性、数据可用性、安全性和性能。这意味着各种规模和行业的客户都可以使用它来存储和保护各种用例(如网站、移动应用程序、备份和还原、存档、企业应用程序、IoT 设备和大数据分析)的任意数量的数据。Amazon S3 提供了易于使用的管理功能,因此您可以组织数据并配置精细调整过的访问控制以满足特定的业务、组织和合规性要求。Amazon S3 可达到 99.999999999%(11 个 9)的持久性,并为全球各地的公司存储数百万个应用程序的数据。

看起来有点长,简单的理解就是amazon提供了文件存储的服务,我们可以用它来做文件存储调amazon`的接口,然后付钱即可。文件管理有他们来维护的这种感觉- -。

下载地址

https://min.io/download

运行

正常是应该使用linux服务器的,无奈家里的环境不满足,这里我只是初步试验,先用windows版试试水。

1
> minio.exe server F:\minio\data

启动后的输出结果如下:

1
2
3
4
5
6
7
8
9
10
Endpoint:  http://192.168.124.13:9000  http://127.0.0.1:9000
AccessKey: minioadmin
SecretKey: minioadmin

Browser Access:
http://192.168.124.13:9000 http://127.0.0.1:9000

......

Detected default credentials 'minioadmin:minioadmin', please change the credentials immediately using 'MINIO_ACCESS_KEY' and 'MINIO_SECRET_KEY'

使用AccessKeySecretKey访问http://localhost:9000,即可进入到web端管理相关文件。

docker版运行方式

1
2
docker pull minio/minio
docker run -p 9000:9000 minio/minio server /data

java SDK

完整API和示例请查看 Java Client API

maven项目中添加如下依赖:

1
2
3
4
5
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>6.0.8</version>
</dependency>

文件上传示例

application.yml中添加服务器相关信息:

1
2
3
4
minio:
endpoint: http://localhost:9000 #minio所在的url
accesskey: minioadmin #用户id
secretkey: minioadmin #密码

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@Component
public class MinioService {

@Value("${minio.endpoint}")
private String endpoint;

@Value("${minio.secretkey}")
private String secretkey;

@Value("${minio.accesskey}")
private String accesskey;

public MinioClient minioClient() throws InvalidPortException, InvalidEndpointException {
return new MinioClient(endpoint, accesskey, secretkey);
}

//检查并创建buckets
public void createBucket(String bucketName) throws Exception {
MinioClient minioClient = minioClient();
boolean exists = minioClient.bucketExists(bucketName);
if (exists) {
System.out.println("bucket is already exists");
} else {
minioClient.makeBucket(bucketName);
//设置策略
minioClient.setBucketPolicy(bucketName, createBucketPolicy().replace("$bucket$", bucketName));
}
}
//上传
public void uploadFile(String bucketName, String objectName, String fileName) throws Exception {
MinioClient minioClient = minioClient();
minioClient.putObject(bucketName, objectName, fileName, null, null, null, null);
}

//配置公共访问和永久可下载策略
private String createBucketPolicy() {
StringBuilder builder = new StringBuilder();
builder.append("{\n");
builder.append(" \"Version\": \"2012-10-17\",\n");
builder.append(" \"Statement\": [{\n");
builder.append(" \"Effect\": \"Allow\",\n");
builder.append(" \"Principal\": {\n");
builder.append(" \"AWS\": [\"*\"]\n");
builder.append(" },\n");
builder.append(" \"Action\": [\"s3:GetBucketLocation\", \"s3:ListBucket\"],\n");
builder.append(" \"Resource\": [\"arn:aws:s3:::$bucket$\"]\n");
builder.append(" }, {\n");
builder.append(" \"Effect\": \"Allow\",\n");
builder.append(" \"Principal\": {\n");
builder.append(" \"AWS\": [\"*\"]\n");
builder.append(" },\n");
builder.append(" \"Action\": [\"s3:GetObject\"],\n");
builder.append(" \"Resource\": [\"arn:aws:s3:::$bucket$/*\"]\n");
builder.append(" }]\n");
builder.append("}");
return builder.toString();
}
}

测试:

1
2
3
4
5
6
7
8
9
@Autowired
MinioService minioService;

@Test
public void uploadFileTest() throws Exception {
minioService.createBucket("test");
minioService.uploadFile("test", "minio.exe", "I:\\minio.exe");
System.out.println("上传成功...");
}

。。。真的超级简单啊。

基础API介绍

bucket操作

  • makeBucket
  • listBuckets
  • bucketExists
  • removeBucket
  • listObjects
  • listIncompleteUploads

相关测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Test
public void bucketOperationTest() throws Exception {
MinioClient minioClient = minioService.minioClient();
LocalDateTime localDateTime = LocalDateTime.now();
String dateFormat = localDateTime.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
//创建bucket
minioService.createBucket("hoppo");
minioService.createBucket("local");
//listBuckets
for (Bucket bucket : minioClient.listBuckets()) {
System.out.println("alreay exists bucket: " + bucket.name());
}
//检查是否存在
System.out.println("bucketName local exists status: " + minioClient.bucketExists("local"));
//remove
minioClient.removeBucket("local");
System.out.println("bucketName local exists status: " + minioClient.bucketExists("local"));
//列出bucket
for (Bucket bucket : minioClient.listBuckets()) {
System.out.println(bucket.name());
}
minioService.uploadFile("hoppo", dateFormat + "/minio.exe", "I:\\minio.exe");
System.out.println("上传成功...");
//列出已有存储对象
for (Result<Item> next : minioClient.listObjects("hoppo")) {
Item item = next.get();
System.out.println(item.objectName());
System.out.println(item.objectSize());
System.out.println(item.lastModified());
}
}

Object存储对象操作

  • getObject
  • putObject
  • copyObject
  • statObject
  • removeObject
  • removeIncompleteUpload
  • getObjectUrl

相关测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Test
void objectOperationTest() throws Exception {
MinioClient minioClient = minioService.minioClient();
LocalDateTime localDateTime = LocalDateTime.now();
String dateFormat = localDateTime.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
//getObject下载
InputStream hoppo = minioClient.getObject("hoppo", dateFormat + "/minio.exe");
FileOutputStream outputStream = new FileOutputStream("F:\\minio.exe");

byte[] buffer = new byte[1024];
while ((hoppo.read(buffer)) != -1) {
outputStream.write(buffer);
}
outputStream.close();
hoppo.close();
//putObject上传
// minioService.uploadFile("hoppo", dateFormat + "/minio.exe", "I:\\minio.exe");
//copyObject,复制一份到test
minioClient.copyObject("hoppo", dateFormat + "/minio.exe", "test", dateFormat + "/minio.exe");

//获取指定存储对象的信息
ObjectStat minioexe = minioClient.statObject("hoppo", dateFormat + "/minio.exe");
System.out.println(minioexe.contentType());
System.out.println(minioexe.name());
//获取下载链接,该方式获得的链接必须是公共可下载的,
String hoppoUrl = minioClient.getObjectUrl("hoppo", dateFormat + "/minio.exe");
System.out.println(hoppoUrl);
//移除存储对象
//minioClient.removeObject("hoppo",dateFormat + "/minio.exe");
}

Presign操作

presignedGetObject

public String presignedGetObject(String bucketName, String objectName, Integer expires)

生成一个HTTP GET请求用的presigned URL浏览器/移动端的客户端可以用这个URL进行下载即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。

1
2
3
4
5
6
try {
String url = minioClient.presignedGetObject("mybucket", "myobject", 60 * 60 * 24);
System.out.println(url);
} catch(MinioException e) {
System.out.println("Error occurred: " + e);
}

presignedPutObject

public String presignedPutObject(String bucketName, String objectName, Integer expires)

生成一个给HTTP PUT请求用的presigned URL浏览器/移动端的客户端可以用这个URL进行上传,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天

1
2
3
4
5
6
try {
String url = minioClient.presignedPutObject("mybucket", "myobject", 60 * 60 * 24);
System.out.println(url);
} catch(MinioException e) {
System.out.println("Error occurred: " + e);
}

presignedPostPolicy

public Map<String,String> presignedPostPolicy(PostPolicy policy)

允许给POST请求的presigned URL设置策略,比如接收对象上传的存储桶名称的策略,key名称前缀,过期策略。

1
2
3
4
5
6
7
8
9
10
11
try {
PostPolicy policy = new PostPolicy("mybucket", "myobject",DateTime.now().plusDays(7));
policy.setContentType("image/png");
Map<String,String> formData = minioClient.presignedPostPolicy(policy);
System.out.print("curl -X POST ");
for (Map.Entry<String,String> entry : formData.entrySet()) {
System.out.print(" -F " + entry.getKey() + "=" + entry.getValue());
}
System.out.println(" -F file=@/tmp/userpic.png https://play.min.io/mybucket");
} catch(MinioException e) {
System.out.println("Error occurred: " + e);

BucketPolicy 操作

  • getBucketPolicy
  • setBucketPolicy

get不再贴出来了,set的实例方法如下所示:(将my-bucketname替换为自己的bucketName)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class SetBucketPolicy {
/**
* 设置bucket策略,允许永久下载。minio 默认连接 7 天过期
*/
public static void main(String[] args)
throws IOException, NoSuchAlgorithmException, InvalidKeyException, XmlPullParserException {
try {
StringBuilder builder = new StringBuilder();
builder.append("{\n");
builder.append(" \"Version\": \"2012-10-17\",\n");
builder.append(" \"Statement\": [{\n");
builder.append(" \"Effect\": \"Allow\",\n");
builder.append(" \"Principal\": {\n");
builder.append(" \"AWS\": [\"*\"]\n");
builder.append(" },\n");
builder.append(" \"Action\": [\"s3:GetBucketLocation\", \"s3:ListBucket\"],\n");
builder.append(" \"Resource\": [\"arn:aws:s3:::my-bucketname\"]\n");
builder.append(" }, {\n");
builder.append(" \"Effect\": \"Allow\",\n");
builder.append(" \"Principal\": {\n");
builder.append(" \"AWS\": [\"*\"]\n");
builder.append(" },\n");
builder.append(" \"Action\": [\"s3:GetObject\"],\n");
builder.append(" \"Resource\": [\"arn:aws:s3:::my-bucketname/*\"]\n");
builder.append(" }]\n");
builder.append("}");
minioClient.setBucketPolicy("my-bucketname", builder.toString());
} catch (MinioException e) {
System.out.println("Error occurred: " + e);
}
}
}

相关网站

实战秘籍 - 《Minio Cookbook 中文版》 - 书栈网 · BookStack

Java Client API参考文档

0%