SpringBoot整合MinIO-Spring专区论坛-技术-SpringForAll社区

SpringBoot整合MinIO

[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 & 

确认是否启动成功

启动输出信息:


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-WebUI

本文仅做演示,不对 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");
    }
   
}

引用

请登录后发表评论