MinIO安装和使用

MinIO介绍

  • MinIO基于Apache License v2.0开源协议的对象存储服务,可以做为云存储的解决方案用来保存海量的图片,视频,文档

  • 由于采用Golang实现,服务端可以工作在Windows,Linux, OS X和FreeBSD上

  • 配置简单,基本是复制可执行程序,单行命令可以运行起来

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

  • 分布式文件系统(存储)方案

    付费云服务

  • 阿里云OSS
  • 七牛云存储
  • 腾讯云存储
  • 华为云存储
  • 免费的私有化服务

  • 阿里的FastDFS
  • MinIO
  • MinIO的一些概念

  • bucket (桶)– 类比于文件系统的目录
  • Object – 类比文件系统的文件
  • Keys – 类比文件名
  • 官网文档: http://docs.minio.org.cn/docs/

    MinIO的特点

  • Minio使用Minio Erasure Code(纠删码)来防止硬件故障。即便损坏一半以上的driver,但是仍然可以从中恢复
  • 作为高性能对象存储,在标准硬件条件下它能达到55GB/s的读、35GB/s的写速率
  • 不同MinIO集群可以组成联邦,并形成一个全局的命名空间,并跨越多个数据中心
  • SDK支持

  • 基于Minio轻量的特点,它得到类似Java、Python或Go等语言的sdk支持
  • 有操作页面

  • 面向用户友好的简单操作界面,非常方便的管理Bucket及里面的文件资源
  • 这一设计原则让MinIO不容易出错、更快启动
  • 丰富的API

  • 支持文件资源的分享连接及分享链接的过期策略、存储桶操作、文件列表访问及文件上传下载的基本功能等
  • 文件变化主动通知

  • 存储桶(Bucket)如果发生改变,比如上传对象和删除对象,可以使用存储桶事件通知机制进行监控,并通过以下方式发布出去:AMQP、MQTT、Elasticsearch、Redis、NATS、MySQL、Kafka、Webhooks等
  • MinIO的安装

  • 安装最新版
  • docker pull minio/minio
    
  • 安装指定版本
  • docker pull minio/minio:RELEASE.2022-06-20T23-13-45Z.fips
    
  • 设置Minio的登录账号为minio,密码为minio123
    docker run -d -p 9000:9000 -p 50000:50000 --name minio -e "MINIO_ROOT_USER=minio" -e "MINIO_ROOT_PASSWORD=minio123" -v /home/data:/data -v /home/config:/root/.minio minio/minio server --console-address ":50000" /data
    

    登录管理后台

  • Minio的后台管理系统的端口为50000,ip + 端口,输入上面的账号和密码,点击登录,即可进入登录界面
  • http://192.168.253.133:50000
    

    基于SpringBoot工程,学习MinIO的使用

  • 添加MinIO依赖
  • <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.zh</groupId>
        <artifactId>minio-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
        <!-- 继承Spring boot工程 -->
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.8.RELEASE</version>
        </parent>
        <dependencies>
            <!-- MinIO依赖 -->
            <dependency>
                <groupId>io.minio</groupId>
                <artifactId>minio</artifactId>
                <version>7.1.0</version>
            </dependency>
            <!-- SpringBoot的Web启动器 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!-- SpringBoot的测试启动器 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
            </dependency>
        </dependencies>
    </project>
    
    @SpringBootApplication
    public class MinIOApplication {
        public static void main(String[] args) {
            SpringApplication.run(MinIOApplication.class, args);
    
  • 新建测试类,定义MinIO地址、账号、密码的公共常量
  • public class MinioTest {
         * minio 服务器地址
        private static final String MINIO_SERVER_ADDRESS = "http://192.168.253.133:9000";
        private final String ACCESS_KEY = "minio";
        private final String SECRET_KEY = "minio123";
    
    @Test
    public void testUpload() throws Exception {
        //创建MinioClient对象
        MinioClient minioClient = MinioClient.builder()
                //服务器地址
                .endpoint(MINIO_SERVER_ADDRESS)
                //账号和密码
                .credentials(ACCESS_KEY, SECRET_KEY)
                .build();
        //准备文件流
        FileInputStream inputStream = new FileInputStream("c:/test/list-1.html");
        //准备上传参数
        PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                //bucket名称
                .bucket("leadnews")
                //在bucket的名称
                .object("list-test.html")
                //参数一:文件流 参数二:文件大小 参数三:部分大小,-1代表整个文件大小
                .stream(inputStream, inputStream.available(), -1)
                //文件类型
                .contentType("text/html")
                .build();
        //开始上传
        minioClient.putObject(putObjectArgs);
        //关闭流
        inputStream.close();
    
    @Test
    public void testDownload() throws Exception {
        //创建MinioClient对象
        MinioClient minioClient = MinioClient.builder()
                //服务器地址
                .endpoint(MINIO_SERVER_ADDRESS)
                //账号和密码
                .credentials(ACCESS_KEY, SECRET_KEY)
                .build();
        GetObjectArgs getObjectArgs = GetObjectArgs.builder()
                .bucket("leadnews")
                .object("list-test.html")
                .build();
        //读取minio中的文件,使用流
        InputStream inputStream = minioClient.getObject(getObjectArgs);
        //将文件写出到磁盘
        FileOutputStream outputStream = new FileOutputStream("c:/test/list-test.html");
        //保存到磁盘
        byte[] buf = new byte[1024];
        int len;
        while ((len = inputStream.read(buf)) != -1) {
            outputStream.write(buf, 0, len);
        //关闭流
        outputStream.close();
        inputStream.close();
    
    @Test
    public void removeFile() throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {
        MinioClient minioClient = MinioClient.builder()
                //服务器地址
                .endpoint(MINIO_SERVER_ADDRESS)
                //账号和密码
                .credentials(ACCESS_KEY, SECRET_KEY)
                .build();
        RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder()
                .bucket("leadnews")
                //文件夹
                .object("list-test.html")
                .build();
        //发起删除
        minioClient.removeObject(removeObjectArgs);
    

    封装MinIO

    MinIO的MinioClient应该是一个单例存放在IOC容器,MinIO的配置应该存放在配置文件中,但MinIO没有提供SpringBoot的Starter启动器依赖,所以需要自己封装

  • 封装MinIO配置属性类,使用@ConfigurationProperties注解,定义一个前缀,在yml配置文件中统一配置的前缀,并且配置文件中的属性,自动映射到配置属性类的类属性上
  • * 定义MinIO的参数属性类 @Data //声明为配置属性类,加入IOC容器,只会加载yml文件中的指定,前缀下的属性 @ConfigurationProperties(prefix = "minio") public class MinioProperties { * 账户名称 private String accessKey; * 账户密码 private String secretKey; * MinIO连接地址,例:http://192.168.253.133:9000 private String endpoint; * 桶名称 private String bucket; * 访问文件的地址,例:http://192.168.253.133:9000 private String readPath;
  • 封装MinIO的配置类,导入MinioProperties配置属性类,并声明MinioClient到Spring的IOC容器中
  • * 初始MinIO连接对象配置类 @Configuration //加载配置属性类 @EnableConfigurationProperties(MinioProperties.class) public class MinIOConfiguration { * 注入属性配置类 @Autowired private MinioProperties minIOConfigProperties; * 将Minio的客户端对象,加入IOC容器 @Bean public MinioClient createMinioClient() { return MinioClient.builder() //用户名和密码 .credentials(minIOConfigProperties.getAccessKey(), minIOConfigProperties.getSecretKey()) //服务器地址 .endpoint(minIOConfigProperties.getEndpoint()) .build();
  • 定义MinIO工具类,通过@Component注解,将工具类放入IOC容器,后面使用,就可以使用@Autowired注解进行注入该对象

  • uploadImgFile()方法,上传图片

  • uploadHtmlFile()方法,上传HTML文件

  • downLoadFile()方法,下载文件

  • delete()方法,删除文件

  • @Component
    @Slf4j
    public class MinIOFileStorageService {
         * Minio客户端
        @Autowired
        private MinioClient minioClient;
         * Minio配置属性类
        @Autowired
        private MinioProperties minIOConfigProperties;
         * 分隔符
        private final static String separator = "/";
         * 拼接上年月日,生成文件地址,方便分类管理
         * @param dirPath  目录地址
         * @param filename yyyy/mm/dd/file.jpg
         * @return 新文件名
        public String builderFilePath(String dirPath, String filename) {
            StringBuilder stringBuilder = new StringBuilder(50);
            if (!StringUtils.isEmpty(dirPath)) {
                stringBuilder.append(dirPath).append(separator);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
            String todayStr = sdf.format(new Date());
            stringBuilder.append(todayStr).append(separator);
            stringBuilder.append(filename);
            return stringBuilder.toString();
         * 上传图片文件
         * @param prefix      文件前缀
         * @param filename    文件名
         * @param inputStream 文件流
         * @return 文件全路径
        public String uploadImgFile(String prefix, String filename, InputStream inputStream) {
            String filePath = builderFilePath(prefix, filename);
            try {
                PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                        .object(filePath)
                        .contentType("image/jpg")
                        .bucket(minIOConfigProperties.getBucket()).stream(inputStream, inputStream.available(), -1)
                        .build();
                minioClient.putObject(putObjectArgs);
                StringBuilder urlPath = new StringBuilder(minIOConfigProperties.getReadPath());
                urlPath.append(separator + minIOConfigProperties.getBucket());
                urlPath.append(separator);
                urlPath.append(filePath);
                return urlPath.toString();
            } catch (Exception ex) {
                log.error("minio put file error.", ex);
                throw new RuntimeException("上传文件失败");
         * 上传html文件
         * @param prefix      文件前缀
         * @param filename    文件名
         * @param inputStream 文件流
         * @return 文件全路径
        public String uploadHtmlFile(String prefix, String filename, InputStream inputStream) {
            String filePath = builderFilePath(prefix, filename);
            try {
                PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                        .object(filePath)
                        .contentType("text/html")
                        .bucket(minIOConfigProperties.getBucket()).stream(inputStream, inputStream.available(), -1)
                        .build();
                minioClient.putObject(putObjectArgs);
                StringBuilder urlPath = new StringBuilder(minIOConfigProperties.getReadPath());
                urlPath.append(separator + minIOConfigProperties.getBucket());
                urlPath.append(separator);
                urlPath.append(filePath);
                return urlPath.toString();
            } catch (Exception ex) {
                log.error("minio put file error.", ex);
                ex.printStackTrace();
                throw new RuntimeException("上传文件失败");
         * 删除文件
         * @param pathUrl 文件全路径
        public void delete(String pathUrl) {
            String key = pathUrl.replace(minIOConfigProperties.getEndpoint() + "/", "");
            int index = key.indexOf(separator);
            String bucket = key.substring(0, index);
            String filePath = key.substring(index + 1);
            // 删除Objects
            RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder().bucket(bucket).object(filePath).build();
            try {
                minioClient.removeObject(removeObjectArgs);
            } catch (Exception e) {
                log.error("minio remove file error.  pathUrl:{}", pathUrl);
                e.printStackTrace();
         * 下载文件
         * @param pathUrl 文件全路径
         * @return 文件流
        public byte[] downLoadFile(String pathUrl) {
            String key = pathUrl.replace(minIOConfigProperties.getEndpoint() + "/", "");
            int index = key.indexOf(separator);
            String bucket = key.substring(0, index);
            String filePath = key.substring(index + 1);
            InputStream inputStream = null;
            try {
                inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(minIOConfigProperties.getBucket()).object(filePath).build());
            } catch (Exception e) {
                log.error("minio down file error.  pathUrl:{}", pathUrl);
                e.printStackTrace();
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            byte[] buff = new byte[100];
            int rc = 0;
            while (true) {
                try {
                    if (!((rc = inputStream.read(buff, 0, 100)) > 0)) break;
                } catch (IOException e) {
                    e.printStackTrace();
                byteArrayOutputStream.write(buff, 0, rc);
            return byteArrayOutputStream.toByteArray();
    
  • 微服务添加MinIO配置
  • minio:
      accessKey: minio
      secretKey: minio123
      bucket: leadnews
      endpoint: http://192.168.253.133:9000
      readPath: http://192.168.253.133:9000
    
  • 添加MinIO配置类,扫描MinIO封装类的包
  • * Minio配置类 @Configuration @ComponentScan("com.zh.common.minio") public class MinioConfig { * Minio测试类 @RunWith(SpringRunner.class) @SpringBootTest(classes = ArticleApplication.class) public class MinioTest { @Autowired private MinIOFileStorageService storageService; * 上传文件 @Test public void testUploadFile() throws Exception { FileInputStream inputStream = new FileInputStream("c:/test/list-1.html"); String url = storageService.uploadHtmlFile("", "list-article-1.html", inputStream); System.out.println(url); * 下载文件 @Test public void testDownloadFile() throws Exception { String url = "http://192.168.253.133:9000/leadnews/2023/01/14/list-article-1.html"; byte[] bytes = storageService.downLoadFile(url); System.out.println(bytes); * 删除文件 @Test public void testDeleteFile() throws Exception {