[toc]
MinIO简介
MinIO是一款基于Go语言开发的高性能、分布式的对象存储系统,开源可商用。一开始就被设计为服务于私有云、公有云、混合云,因此在高可用、可扩展性、高性能方面有得天独厚的优势。
MinIO完全实现了AWS S3 标准,在日常使用、扩展升级、迁移方面更易于管理,对于上层应用程序来说,存储和访问对象是统一的,即使MinIO服务迁移了,应用程序侧是无感知的。
MinIO分三个版本,开源版、标准版、企业版,开源版本免费使用,后面两个为付费产品。MinIO支持多种部署环境:Kubernetes、Docker、Linux、MacOS、Windows
Amazon S3 标准
S3简介
AWS S3 全称是 Simple Storage Service
=_=*
所以就 S3 了? 3S更确切把?
简单的存储服务规范,实际就是一种指导思想,然后大家都按照这种要求来。比如规范要求Bucket 的名称不可以带下划线。
基本概念
- Bucket:桶是最顶层的结构,所有的文件都必须放在桶中,桶的名称需要保持唯一,没有对象存储数量限制
- Object:桶中放的数据就是对象,由对象名称和对象值组成,对象名称可以很长,并可以划分path,对象包含一些元数据,如文件类型、创建时间、用户指定的元信息,对象可以设置标签,标签也是键值对结构,可以修改,标签的作用是可以结合权限控制对象的访问、生命周期的管理、数据分析等,单个文件最大5GB,超过需要使用
multipart upload API (最大支持5TB)
Server端部署
以 CentOS(amd64架构) 为例
下载
打开天朝专属下载地址,在 /server/minio/release/linux-amd64/
目录下有一个minio文件,这个文件是一个可执行,下载这个文件放到服务器上面
运行
官方推荐rpm安装,这种在有互联网的时候很友好,但是无互联网的环境就很麻烦,哪怕可以搭建临时存储库还是麻烦。
本文下载的是可执行文件,方便操作和演示将直接使用 nohup 方式运行。
将下载好的minio可执行文件赋予执行权限,并移动到 /usr/local/bin/
下,这样全局就都可以访问这个可执行文件了,相关命令为:
//赋予执行权限
chmod +x minio
//移动到 /usr/local/bin/ 目录下
sudo mv minio /usr/local/bin/
运行之前,需要创建一个文件夹用来存储上传的文件,本文在 /root 下创建了一个名为 minio 的目录。
mkdir ~/minio
启动 MinIO-Server
nohup minio server ~/minio --console-address :9090 &
确认是否启动成功
- 使用ps命令检查:ps aux |grep minio
- 访问webUI:http://ip:9090/
启动输出信息:
WARNING: Detected Linux kernel version older than 4.0.0 release, there are some known potential performance problems with this kernel version. MinIO recommends a minimum of 4.x.x linux kernel version for best performance
WARNING: Detected default credentials 'minioadmin:minioadmin', we recommend that you change these values with 'MINIO_ROOT_USER' and 'MINIO_ROOT_PASSWORD' environment variables
MinIO Object Storage Server
Copyright: 2015-2023 MinIO, Inc.
License: GNU AGPLv3 <https://www.gnu.org/licenses/agpl-3.0.html>
Version: RELEASE.2023-09-04T19-57-37Z (go1.19.12 linux/amd64)
Status: 1 Online, 0 Offline.
S3-API: http://xxx:9000 http://127.0.0.1:9000
Console: http://xxx:9090 http://127.0.0.1:9090
Documentation: https://min.io/docs/minio/linux/index.html
Warning: The standard parity is set to 0. This can lead to data loss.
启动完毕会占用两个端口:
- 9090:webui
- 9000:S3API 地址,SpringBoot程序通过此地址和MinIOServer交互
本文仅做演示,不对 MinIO-Server 部署做过多介绍,实际生产环境中,推荐使用高可用的部署架构,参见:https://min.io/docs/minio/linux/operations/install-deploy-manage/deploy-minio-multi-node-multi-drive.html#deploy-minio-distributed
常用API
MinIOClientBuilder
endpoint:接收一个url的串或者okhttp3.HttpUrl
credentials:用户名密码或者AccessKey
region:接受S3服务的区域名称。如果指定,则所有操作都使用此区域,否则将按存储桶探测区域。
- httpClient:自定义HTTPclient , 默认为OkHttp
Bucket操作
- bucketExists:桶是否存在
- listBuckets:列出所有的桶
- makeBucket:创建一个桶
- removeBucket:删除一个桶
Object操作
- getPresignedObjectUrl:获取对象访问url
- putObject:上传对象
- uploadObject:上传对象
- removeObject:删除对象
SpringBoot整合
pom
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.2.2</version>
</dependency>
配置MinIO客户端
其中 credentials 需要提前在WebUI中创建Identity-Users或者AccessKeys
package com.ramble.minio.config;
import io.minio.MinioClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MinIoConfig {
@Bean
public MinioClient initMinioClient() {
MinioClient client = MinioClient.builder()
.endpoint("http://192.168.1.46:9000")
//Identity-Users
// .credentials("test", "test123456")
//AccessKeys
.credentials("zh10tpbzJtZX77FtCHyy", "dGz6nXICMOKAvabOc7UyVYmKz2hAiOTfT76Jzu0M")
.build();
return client;
}
}
Service
封装一个 MinIOService ,提供常用的接口服务
package com.ramble.minio.service;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@Service
@RequiredArgsConstructor
public class MinioService {
private final MinioClient minioClient;
/**
* 查看bucket是否存在
*
* @param bucketName
* @return
*/
@SneakyThrows
public Boolean existsBucket(String bucketName) {
return minioClient.bucketExists(BucketExistsArgs.builder()
.bucket(bucketName)
.build());
}
/**
* 创建存储bucket
*
* @return Boolean
*/
@SneakyThrows
public void makeBucket(String bucketName) {
minioClient.makeBucket(MakeBucketArgs.builder()
.bucket(bucketName)
.build());
}
/**
* 删除存储bucket
*
* @return Boolean
*/
@SneakyThrows
public void removeBucket(String bucketName) {
minioClient.removeBucket(RemoveBucketArgs.builder()
.bucket(bucketName)
.build());
}
/**
* 获取全部bucket
*/
@SneakyThrows
public List<Bucket> listBuckets() {
return minioClient.listBuckets();
}
/**
* 获取文件的url
*
* @param fileName
* @return
*/
@SneakyThrows
public String getUrl(String bucketName, String fileName) {
GetPresignedObjectUrlArgs build = GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(fileName).method(Method.GET).build();
return minioClient.getPresignedObjectUrl(build);
}
/**
* 查看文件对象
*
* @return 存储bucket内文件对象信息
*/
@SneakyThrows
public List<Item> listObjects(String bucketName) {
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder().bucket(bucketName).build());
List<Item> items = new ArrayList<>();
for (Result<Item> result : results) {
items.add(result.get());
}
return items;
}
/**
* 删除
*
* @param fileName
* @return
* @throws Exception
*/
@SneakyThrows
public void remove(String bucketName, String fileName) {
minioClient.removeObject(RemoveObjectArgs.builder().
bucket(bucketName)
.object(fileName)
.build());
}
}
Controller
新建一个controller,测试常用的接口
package com.ramble.minio.controller;
import com.ramble.minio.service.MinioService;
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.UploadObjectArgs;
import io.minio.errors.MinioException;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/test")
@RequiredArgsConstructor
public class TestController {
private final MinioClient minioClient;
private final MinioService minioService;
@GetMapping("/test2")
public void test2() {
Boolean existsBucket = minioService.existsBucket("person");
minioService.makeBucket("person");
minioService.makeBucket("person2");
minioService.removeBucket("person2");
List<Bucket> buckets = minioService.listBuckets();
String url = minioService.getUrl("first-bucket", "锁屏幻灯片/0028.jpg");
List<Item> items = minioService.listObjects("first-bucket");
minioService.remove("first-bucket", "Win11_22H2_Chinese_Simplified_x64v2.iso");
}
}