开源利器:打造私有云存储解决方案的首选——MinIO 详解


开源利器:打造私有云存储解决方案的首选——MinIO 详解

Minio 是什么?

Minio是一个开源的、云原生的分布式对象存储系统,是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据。

它一大特点就是轻量,虽然轻量,却拥有着不错的性能。使用简单,功能强大,支持各种平台,单个文件最大5TB,兼容 Amazon S3接口,提供了 JavaPython、GO等多版本SDK支持。

官网:https://www.minio.org.cn

1. 那 MINIO 是对象存储,和 COS 这些有什么区别呢?

MinIO 和 COS(腾讯云对象存储)都是云存储服务,但它们有一些区别。

  1. 提供商

    • MinIO 是一个开源的对象存储服务器,可以在本地或者私有云环境中部署和运行。
    • COS 是由腾讯云提供的对象存储服务,部署在腾讯云的基础设施上。
  2. 定位

    • MinIO 更加注重于提供给开发者一个简单、高性能的对象存储解决方案,特别适合在本地或者私有云环境中构建大规模的数据存储系统。
    • COS 则是一个公共云服务,适用于各种规模的企业和个人用户,提供了稳定、可靠、高可用的对象存储服务。
  3. 功能特性

    • MinIO 提供了丰富的功能,包括高性能、S3 兼容性、分布式存储、数据加密、版本控制等。
    • COS 也提供了类似的功能,同时还有一些与腾讯云其他服务集成的特性,比如 CDN 加速、数据迁移等。
  4. 适用场景

    • 如果你需要在本地或者私有云环境中构建高性能、可扩展的对象存储系统,那么 MinIO 是一个很好的选择。
    • 如果你需要在公共云环境中部署应用,或者需要与其他腾讯云服务集成,那么 COS 是一个更合适的选择。

如果你需要自己控制基础设施并且注重性能和灵活性,可以考虑使用 MinIO;

如果你需要一个稳定、可靠的公共云对象存储服务,或者需要与腾讯云其他服务集成,那么 COS 是一个不错的选择。

Windows 环境安装 MINIO:

1. 下载服务端和客户端安装包文件

开源利器:打造私有云存储解决方案的首选——MinIO 详解

下载地址:https://min.io/download#/agpl-kubernetes

服务端文件:minio.exe 用于接收文件信息

客户端文件:mac.exe 用于上传文件,如果用程序代码操作文件存储,只启动服务端就 ok。

2. 创建 MINIO 目录(不强求、但推荐这样做)

1、创建一个 minio 的文件夹,存放 minio 相关文件;

2、在 minio 文件夹中创建 minio 的应用目录 bin,存放相关数据文件;

3、在 minio 文件夹中创建 minio 的数据目录 data,存放相关数据文件;

4、在 minio 文件夹中创建 minio 的数据目录 log,存储相关日志

3. 启动 minio 服务器

启动方式有两种,个人比较推荐第一种方式:

1、命令启动

2、新建 .bat 脚本文件启动

特别提示:在 Windows 安装软件我们都习惯双击 .exe 文件启动,minio 中可不行,可能会导致服务最终启动失败;所以无论是 windows 还是 Linux 都建议通过命令启动。

命令启动:

以管理员权限打开 cmd 窗口,进入到 minio.exe 所在 bin 目录

minio.exe server D:minIO

或者

.minio.exe server D:develpominiodata --console-address "127.0.0.1:9000" --address "127.0.0.1:9005"


开源利器:打造私有云存储解决方案的首选——MinIO 详解

服务启动之后会出现服务的账号密码,不提前进行设置的话会显示默认密码,但是设置密码也非常的简单:


cmd 进入到 minio 的 bin 目录下,执行下述操作:

setx MINIO_ROOT_USER name  // 设置登录账号
setx MINIO_ROOT_PASSWORD password  // 设置登录密码

4. 访问 minio 服务器

开源利器:打造私有云存储解决方案的首选——MinIO 详解

http://127.0.0.1:9000/ 输入用户密码即可。

简单介绍一下可视化界面:

开源利器:打造私有云存储解决方案的首选——MinIO 详解

可用存储桶列表

开源利器:打造私有云存储解决方案的首选——MinIO 详解

生成密钥

开源利器:打造私有云存储解决方案的首选——MinIO 详解

存储桶详细信息

开源利器:打造私有云存储解决方案的首选——MinIO 详解

存储桶已有的文件

使用 Spring 整合 MINIO

说了这么多,到底应该怎么使用 Spring 去操作 MINIO 呢?

1. 创建 Spring 工程并引入依赖

<!-- MINIO 对象存储 -->
<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>8.3.5</version>
</dependency>
<!-- okhttp3(MINIO 对象存储 依赖) -->
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.8.1</version>
</dependency>

2. 配置上传信息

minio:
  bucket-name: test-bucket
  endpoint: http://192.168.31.18:9000
  access-key: accessKey
  secret-key: secretKey

accessKey 和 secretKey 生成方法在上面有强调过。

3. 编写 minio 属性值

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * minio 属性值
 */

@Data
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioProperties {
    /** 连接url */
    @Value("${minio.endpoint}")
    private String endpoint;

    /** 公钥 */
    @Value("${minio.access-key}")
    private String accessKey;

    /** 私钥 */
    @Value("${minio.secret-key}")
    private String secretKey;

    /** 桶名 */
    @Value("${minio.bucket-name}")
    private String bucketName;
}

4. 编写 minio 核心配置类

import com.example.projectBudget.common.entity.MinioProperties;
import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * minio核心配置类
 * 通过注入 MinIO 服务器的相关配置信息,得到 MinioClient 对象,我们上传文件依赖此对象
 */

@Configuration
public class MinioConfig {

    @Autowired
    private MinioProperties prop;

    /**
     * 获取 MinioClient
     *
     * @return MinioClient
     */

    @Bean
    public MinioClient minioClient() {
        return MinioClient.builder().endpoint(prop.getEndpoint()).credentials(prop.getAccessKey(), prop.getSecretKey())
            .build();
    }
}

5. 编写 minio 工具类

import com.alibaba.excel.util.CollectionUtils;
import com.example.projectBudget.common.entity.MinioProperties;
import com.example.projectBudget.common.exceptions.MyException;
import io.micrometer.core.instrument.util.StringUtils;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class MinioUtils {
    @Autowired
    private MinioClient minioClient;
    @Autowired
    private MinioProperties minioProperties;

    /**
     * 创建桶
     *
     * @param bucketName 桶名称
     */

    @SneakyThrows
    public void createBucket(String bucketName) {
        boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (!found) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).region("cn-beijing").build());
        }
    }

    /**
     * 删除桶
     *
     * @param bucketName 桶名称
     */

    @SneakyThrows
    public void removeBucket(String bucketName) {
        minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
    }

    /**
     * 上传文件
     *
     * @param fileName 文件名
     * @param stream   流
     * @param fileSize 文件大小
     * @param type     文件类型
     * @throws Exception
     */

    public void uploadFile(String bucketName, String fileName, InputStream stream, Long fileSize, String type)
            throws Exception 
{
        minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(fileName).stream(stream, fileSize, -1)
                .contentType(type).build());
    }

    /**
     * 下载文件
     *
     * @param bucketName 存储桶名称
     * @param fileName   文件名
     * @param outputStream 输出流
     * @throws Exception
     */

    public void downloadFile(String bucketName, String fileName, OutputStream outputStream)
            throws Exception 
{
        // 获取对象并将其写入输出流
        try (InputStream inputStream = minioClient.getObject(
                GetObjectArgs.builder()
                        .bucket(bucketName)
                        .object(fileName)
                        .build())) {
            byte[] buf = new byte[16384];
            int bytesRead;
            while ((bytesRead = inputStream.read(buf, 0, buf.length)) >= 0) {
                outputStream.write(buf, 0, bytesRead);
            }
        }
    }

    /**
     * 判断文件夹是否存在
     *
     * @param bucketName 桶名称
     * @param prefix     文件夹名字
     * @return
     */

    @SneakyThrows
    public Boolean folderExists(String bucketName, String prefix) {
        Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(
                prefix).recursive(false).build());
        for (Result<Item> result : results) {
            Item item = result.get();
            if (item.isDir()) {
                return true;
            }
        }
        return false;
    }

    /**
     * 创建文件夹
     *
     * @param bucketName 桶名称
     * @param path       路径
     */

    @SneakyThrows
    public void createFolder(String bucketName, String path) {
        minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(path)
                .stream(new ByteArrayInputStream(new byte[]{}), 0, -1).build());
    }

    /**
     * 获取文件在minio在服务器上的外链
     *
     * @param bucketName 桶名称
     * @param objectName 文件名
     * @return 可访问的URL
     * expiry(int duration, TimeUnit unit) 设置有效时间,这里是设置为URL的有效期为1天,即在一天以后国企.
     */

    @SneakyThrows
    public String getObjectUrl(String bucketName, String objectName) {
        return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(
                bucketName).object(objectName).expiry(1, TimeUnit.DAYS).build());
    }

    /**
     * 生成可访问的url
     *
     * @param filePath 文件路径
     * @return 可访问的文件, 过期时间1day
     * @author HuangLongFei
     * @since 2023/3/11
     */

    public String generateUrl(String filePath) {
        return getObjectUrl(minioProperties.getBucketName(), filePath);
    }

    /**
     * 批量删除文件
     *
     * @param filePathList 文件路径列表
     * @author HuangLongFei
     * @since 2023/7/17
     */

    public void deleteFile(List<String> filePathList) throws MyException {
        if (CollectionUtils.isEmpty(filePathList)) {
            return;
        }
        // 构建要删除的文件列表
        List<DeleteObject> deleteFileList = new ArrayList<>();
        for (String filePath : filePathList) {
            deleteFileList.add(new DeleteObject(filePath));
        }
        try {
            // 批量删除文件
            RemoveObjectsArgs fileArgs = RemoveObjectsArgs.builder().bucket(minioProperties.getBucketName()).objects(
                    deleteFileList).build();
            Iterable<Result<DeleteError>> fileDeleteResult = minioClient.removeObjects(fileArgs);
            // 注意这里必须迭代返回结果,因为removeObjects是惰性的
            for (Result<DeleteError> result : fileDeleteResult) {
                DeleteError deleteError = result.get();
            }
        } catch (Exception e) {
            throw new MyException("MINIO_CALL_ERROR");
        }
    }

    /**
     * 获取minio中,某个bucket中所有的文件名
     */

    public List<String> getFileList(String bucketName) {
        // 根据bucketName,获取该桶下所有的文件信息
        Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName)
                .recursive(true).build());
        List<String> fileNameList = new ArrayList<>();
        for (Result<Item> result : results) {
            Item item;
            String fileName = null;
            try {
                item = result.get();
                fileName = item.objectName();
                fileNameList.add(fileName);
            } catch (Exception e) {
                log.error("call the minioClient.listObjects Error. result={} and message = {}", result, e);
            }
            // 获取文件的访问路径
            if (StringUtils.isNotBlank(fileName)) {
                String accessUrl = getObjectUrl(bucketName, fileName);
            }
        }
        return fileNameList;
    }

6. 编写测试方法

@Autowired
private MinioUtils minioUtils;
@Autowired
private MinioProperties minioProperties;
private final static String BUCKET_NAME_TEST = "test-bucket";

/***
 * 测试目的:创建一个bucket
 */

@Test
public void createBucket() {
    String bucketName = BUCKET_NAME_TEST;
    minioUtils.createBucket(bucketName);
    System.out.println("create " + bucketName + " success!");
}

/***
 * 测试目的:删除一个bucket
 */

@Test
public void removeBucket() {
    String bucketName = BUCKET_NAME_TEST;
    minioUtils.removeBucket(bucketName);
    System.out.println("remove " + bucketName + " success!");
}

/***
 * 测试目的:创建文件夹,并上传路径
 */

@Test
public void uploadFile() {
    String filePath = "C:/Users/ADMIN/Desktop/aaa.pdf";
    File file = new File(filePath);
    // 设置要上传的路径,例如 /test/2023/123.jpg

    filePath = "/test/2023/" + file.getName();
    try {
        FileInputStream fileInputStream = new FileInputStream(file);
        minioUtils.uploadFile(BUCKET_NAME_TEST, filePath, fileInputStream, file.length(), "application/pdf");
        System.out.println("上传成功!,文件路径:" + filePath);
    } catch (Exception e) {
        System.out.println("上传失败");
        e.printStackTrace();
    }
}

/**
 * 测试目的: 下载文件
 */

@Test
public void downloadFileTest() {
    // 设置下载文件的存储桶和路径
    String bucketName = "test-bucket"// 替换为您的存储桶名称
    String filePath = "/test/2023/aaa.pdf"// 替换为您上传的文件路径

    // 设置下载文件的本地保存路径
    String localFilePath = "C:/Users/ADMIN/Desktop/downloaded_file.pdf"// 替换为您希望保存的本地路径

    try {
        // 创建输出流
        OutputStream outputStream = new FileOutputStream(localFilePath);
        // 下载文件
        minioUtils.downloadFile(bucketName, filePath, outputStream);
        // 关闭输出流
        outputStream.close();

        // 检查文件是否成功下载,假设文件存在且非空
        assertTrue("下载文件失败"new File(localFilePath).exists());
        System.out.println("下载成功!, 文件保存路径:" + localFilePath);
    } catch (Exception e) {
        System.out.println("下载失败");
        e.printStackTrace();
    }
}


/***
 * 测试目的:生成可访问的图片的URL
 */

@Test
public void getObjectUrl() {
    String filePath = "D:/app/webfile/598889851944697856/pdf.png";
    String url = minioUtils.getObjectUrl(BUCKET_NAME_TEST, filePath);
    System.out.println(url);
}

到这里 Spring 整合 MINIO 对象存储实现基本功能就完成啦~ 上传之后就可以根据桶名以及文件在桶中的路径进行在线访问了,比如:http://127.0.0.1:9000/test-bucket/test/2023/helloworld.jpg

开源利器:打造私有云存储解决方案的首选——MinIO 详解

在线访问

但是要在在线访问之前先要设置存储桶的状态为公开:

开源利器:打造私有云存储解决方案的首选——MinIO 详解

原文始发于微信公众号(Coder香):开源利器:打造私有云存储解决方案的首选——MinIO 详解

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/276403.html

(0)
孤翁的头像孤翁

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!