MinIO 基本使用以及在SpringBoot中的应用

MinIO 的基本使用

MinIO 简介

MinIO是一个基于Apache License v2.0开源协议的分布式对象存储服务。兼容亚马逊S3云存储接口,它可以轻松地部署在本地、私有云或公共云环境中,并提供高性能的存储解决方案。

MinIO中的基础概念

  • S3 ,是Simple Storage Service简单对存储的简称,S3提供了一个简单的服务接口,可随时的在web上的任何位置存储和检索任何数量的数据;
  • Obejct ,MinIO存储的基本单位,任何存在MinIO中的事物都可以称之为一个对象(Object),如文档、图片等;
  • Bucket,MinIO中一组对象的逻辑上的隔离单位,不同Bucket中的Object是相互隔离的;
  • Drive,MinIO存储的物理磁盘位置,MinIO中的数据都存在Drive中;
  • Set,一组Drive的集合,在分布式的环境中,自动规划一个或者多个Set,每个Set中的Drive分布在不同的位置,容灾处理。

MinIO安装

MinIO Server 安装

# 下载 minio server
wget https://dl.min.io/server/minio/release/linux-amd64/minio
# 添加可执行权限
chmod +x minio
sudo mv minio /usr/local/bin/
# 创建数据文件夹
mkdir ~/minio
# 启动minio server
minio server ~/minio --console-address :9090
# 启动输出结果
Formatting 1st pool, 1 set(s), 1 drives per set.
WARNING: Host local has more than 0 drives of set. A host failure will result in data becoming unavailable.
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-2022 MinIO, Inc.
License: GNU AGPLv3 <https://www.gnu.org/licenses/agpl-3.0.html>
Version: RELEASE.2022-10-29T06-21-33Z (go1.19.2 linux/amd64)

Status:         1 Online, 0 Offline.
API: http://172.16.159.228:9000  http://172.17.0.1:9000  http://172.18.0.1:9000  http://127.0.0.1:9000
RootUser: minioadmin
RootPass: minioadmin
Console: http://172.16.159.228:9090 http://172.17.0.1:9090 http://172.18.0.1:9090 http://127.0.0.1:9090
RootUser: minioadmin
RootPass: minioadmin

Command-line: https://min.io/docs/minio/linux/reference/minio-mc.html#quickstart
   $ mc alias set myminio http://172.16.159.228:9000 minioadmin minioadmin

Documentation: https://min.io/docs/minio/linux/index.html

访问web admin

从启动的日志输出中得到默认的用户名/密码(minioadmin/minioadmin)和web访问地址 http://127.0.0.1:9000, 使用用户和密码进行登录,第一次登录后建议修改默认的minioadmin密码,左侧菜单 Identity -> ServiceAccount

在这里插入图片描述

MinIO Client 安装

# 下载
wget https://dl.min.io/client/mc/release/linux-amd64/mc
# 添加执行权限
chmod +x mc
mv mc /usr/local/bin/mc
# 添加minio
mc alias set local http://localhost:9000 minioadmin minioadmin
# 添加成功输出信息
mc: Configuration written to `/root/.mc/config.json`. Please update your access credentials.
mc: Successfully created `/root/.mc/share`.
mc: Initialized share uploads `/root/.mc/share/uploads.json` file.
mc: Initialized share downloads `/root/.mc/share/downloads.json` file.

MinIO Client 操作

MinIO Client (mc)为ls,cat,cp,mirror,diff,find等UNIX命令提供了一种替代方案。它支持文件系统和兼容Amazon S3的云存储服务(AWS Signature v2和v4)。

mc命令

ls       列出文件和文件夹。
mb       创建一个存储桶或一个文件夹。
cat      显示文件和对象内容。
pipe     将一个STDIN重定向到一个对象或者文件或者STDOUT。
share    生成用于共享的URL。
cp       拷贝文件和对象。
mirror   给存储桶和文件夹做镜像。
find     基于参数查找文件。
diff     对两个文件夹或者存储桶比较差异。
rm       删除文件和对象。
events   管理对象通知。
watch    监听文件和对象的事件。
policy   管理访问策略。
session  为cp命令管理保存的会话。
config   管理mc配置文件。
update   检查软件更新。
version  输出版本信息。

mb 创建Bucket

# 在local上创建一个test的Bucket
mc mb local/test

Bucket created successfully `local/test`.

cp 拷贝一个对象到MinIO Server中

echo 'hello minio'> hellominio.txt
mc cp hellominio.txt local/test

/root/hellominio.txt:                                 12 B / 12 B

ls 查看Bucket和Obeject

mc ls local
# 刚刚创建的 test Bucket 中存在一个 hellominio.txt
[2022-11-05 20:32:30 CST]    12B STANDARD hellominio.txt

通过MinIO server web 查看
在这里插入图片描述

cat 显示对象的内容

mc cat local/test/hellominio.txt

hello minio

pipe 通过pipe将stdin输出到对象

echo 'pipe test'> pipe.txt
# 将输出重定向到local/test/pipe.txt
cat pipe.txt | mc pipe local/test/pipe.txt
# 查看生成的对象内容
mc cat local/test/pipe.txt

pipe test

find 查找Object

# 查找所有 txt 文本文件
mc find local/test --name "*.txt"
local/test/hellominio.txt
local/test/pipe.txt

share 将对象以带有认证和过期机制的URL机制共享

share download 共享下载

share download命令生成不需要access key和secret key即可下载的URL,过期参数设置成最大有效期(不大于7天),过期之后权限自动回收。

# 为hellominio.txt生成有限期3天的URL
 mc share download --expire 72h local/test/hellominio.txt
URL: http://localhost:9000/test/hellominio.txt
Expire: 3 days 0 hours 0 minutes 0 seconds
Share: http://localhost:9000/test/hellominio.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=minioadmin%2F20221105%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20221105T131412Z&X-Amz-Expires=259200&X-Amz-SignedHeaders=host&X-Amz-Signature=fd3df574faf2276ff47995a361ee1260093bee273d546c76d9ca9f955cc04cdd

share upload 共享上传

share upload命令生成不需要access key和secret key即可上传的URL。过期参数设置成最大有效期(不大于7天),过期之后权限自动回收。 Content-type参数限制只允许上传指定类型的文件。

# 生成上传helloworld
mc share upload local/test/helloword.txt

RL: http://localhost:9000/test/helloword.txt
Expire: 7 days 0 hours 0 minutes 0 seconds
Share: curl http://localhost:9000/test/ -F x-amz-credential=minioadmin/20221105/us-east-1/s3/aws4_request -F x-amz-date=20221105T132630Z -F x-amz-signature=cfd000de2c2494c7fed1c232e5b0a41bb8fb4c7c96a4cb595506910bb7a6a23d -F bucket=test -F policy=eyJleHBpcmF0aW9uIjoiMjAyMi0xMS0xMlQxMzoyNjozMC41MjRaIiwiY29uZGl0aW9ucyI6W1siZXEiLCIkYnVja2V0IiwidGVzdCJdLFsiZXEiLCIka2V5IiwiaGVsbG93b3JkLnR4dCJdLFsiZXEiLCIkeC1hbXotZGF0ZSIsIjIwMjIxMTA1VDEzMjYzMFoiXSxbImVxIiwiJHgtYW16LWFsZ29yaXRobSIsIkFXUzQtSE1BQy1TSEEyNTYiXSxbImVxIiwiJHgtYW16LWNyZWRlbnRpYWwiLCJtaW5pb2FkbWluLzIwMjIxMTA1L3VzLWVhc3QtMS9zMy9hd3M0X3JlcXVlc3QiXV19 -F x-amz-algorithm=AWS4-HMAC-SHA256 -F key=helloword.txt -F file=@<FILE>

# 通过生成的URL上传
echo 'hello world' >helloworld.txt
#  -F file=./helloworld.txt
curl http://localhost:9000/test/ -F x-amz-credential=minioadmin/20221105/us-east-1/s3/aws4_request -F x-amz-date=20221105T132630Z -F x-amz-signature=cfd000de2c2494c7fed1c232e5b0a41bb8fb4c7c96a4cb595506910bb7a6a23d -F bucket=test -F policy=eyJleHBpcmF0aW9uIjoiMjAyMi0xMS0xMlQxMzoyNjozMC41MjRaIiwiY29uZGl0aW9ucyI6W1siZXEiLCIkYnVja2V0IiwidGVzdCJdLFsiZXEiLCIka2V5IiwiaGVsbG93b3JkLnR4dCJdLFsiZXEiLCIkeC1hbXotZGF0ZSIsIjIwMjIxMTA1VDEzMjYzMFoiXSxbImVxIiwiJHgtYW16LWFsZ29yaXRobSIsIkFXUzQtSE1BQy1TSEEyNTYiXSxbImVxIiwiJHgtYW16LWNyZWRlbnRpYWwiLCJtaW5pb2FkbWluLzIwMjIxMTA1L3VzLWVhc3QtMS9zMy9hd3M0X3JlcXVlc3QiXV19 -F x-amz-algorithm=AWS4-HMAC-SHA256 -F key=helloword.txt -F file=./helloworld.txt
# 查看是否上传成功
mc ls local/test
[2022-11-05 20:52:32 CST]    12B STANDARD hellominio.txt
[2022-11-05 21:27:49 CST]    16B STANDARD helloword.txt
[2022-11-05 20:41:34 CST]    10B STANDARD pipe.txt

share list 查看当前的share列表

# share download list
mc share list download
# share upload list
mc share list upload

rm 删除Bucket和Object

# 删除一个对象
mc rm local/test/hellominio.txt
Removed `local/test/hellominio.txt`

# 删除一个Bucket及其中的所有对象
mc rm --recursive --force local/test
Removed `local/test/hellominio.txt`.
Removed `local/test/helloword.txt`.
Removed `local/test/pipe.txt`.

# 按时间删除
# 删除一天前的对象
mc rm --force --older-than=1d local/test/

更多命令使用参考官方使用文档,MinIO Client指南

SpringBoot集成MinIO

添加依赖

  • minio 依赖
<dependency>
  <groupId>io.minio</groupId>
  <artifactId>minio</artifactId>
  <version>8.2.1</version>
</dependency>
  • 完整 pom 文件
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.autumnin</groupId>
    <artifactId>minio-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>minio-demo</name>
    <description>minio-demo</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>8.2.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

  • application.yml
server:
  port: 8080
spring:
  application:
    name: MinIO-demo
  servlet:
    multipart:
      max-file-size: 100MB # 单个文件上传的大小
      max-request-size: 100MB # 设置单次请求的文件的总大小
# MinIO配置
minio:
  endpoint: http://182.92.0.107:9000
  accessKey: minioadmin
  secretKey: minioadmin

Java 代码

  • MinIOConfig配置类
package com.geekyous.minio.config;

import io.minio.MinioClient;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * MinIO配置
 */
@Configuration
@EnableConfigurationProperties(MinIOProperties.class)
public class MinIOConfig {
    private final MinIOProperties properties;

    public MinIOConfig(MinIOProperties properties) {
        this.properties = properties;
    }


    /**
     * 注入 MinIOClient
     *
     * @return MinIOClient
     */
    @Bean
    public MinioClient minioClient() {
        return MinioClient.builder().endpoint(properties.getEndpoint())
        .credentials(properties.getAccessKey(), properties.getSecretKey())
        .build();
    }

}

  • MinIOProperties 配置属性
package com.geekyous.minio.config;

import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * MinIO的配置属性
 */
@ConfigurationProperties(prefix = "minio")
public class MinIOProperties {
    private String endpoint;

    private String accessKey;

    private String secretKey;

    public String getEndpoint() {
        return endpoint;
    }

    public void setEndpoint(String endpoint) {
        this.endpoint = endpoint;
    }

    public String getAccessKey() {
        return accessKey;
    }

    public void setAccessKey(String accessKey) {
        this.accessKey = accessKey;
    }

    public String getSecretKey() {
        return secretKey;
    }

    public void setSecretKey(String secretKey) {
        this.secretKey = secretKey;
    }
}

  • BucketOperator 封装 MinIO 操作类
package com.geekyous.minio.bucket;

import com.geekyous.minio.config.MinIOProperties;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * bucket 操作类
 **/
@Component
public class BucketOperator {
    private final static Logger logger = LoggerFactory.getLogger(BucketOperator.class);
    private final MinioClient minioClient;
    private final MinIOProperties properties;

    public BucketOperator(MinioClient minioClient, MinIOProperties properties) {
        this.minioClient = minioClient;
        this.properties = properties;
    }


    /**
     * 创建给定 bucketName的bucket
     *
     * @param bucketName bucket name
     * @return Result
     */
    public Result create(String bucketName) {
        try {
            if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
                String url = String.format("%s/%s", properties.getEndpoint(), bucketName);
                return Result.builder().url(url).type(Result.Type.BUCKET).state(Result.State.SUCCESS).build();
            } else {
                return Result.builder().type(Result.Type.BUCKET).state(Result.State.FAILED).exception(new RuntimeException(String.format("[%s] bucket already exists", bucketName))).build();
            }
        } catch (Exception e) {
            e.printStackTrace();
            return Result.builder().type(Result.Type.BUCKET).state(Result.State.FAILED).exception(e).build();
        }
    }

    /**
     * 创建给定 bucketName的bucket
     *
     * @param bucketName bucket name
     * @return Result
     */
    public Result createIfNotExists(String bucketName) {
        try {
            if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            } else {
                logger.warn(String.format("[%s] bucket already exists", bucketName));
            }
            String url = String.format("%s/%s", properties.getEndpoint(), bucketName);
            return Result.builder().url(url).type(Result.Type.BUCKET).state(Result.State.SUCCESS).build();
        } catch (Exception e) {
            return Result.builder().type(Result.Type.BUCKET).state(Result.State.FAILED).exception(e).build();
        }
    }


    /**
     * 获取所有的buket
     *
     * @return List<Bucket>
     */
    public Result getAll() {
        try {
            List<Bucket> buckets = minioClient.listBuckets();
            return Result.builder().buckets(buckets).type(Result.Type.BUCKET).state(Result.State.SUCCESS).build();
        } catch (Exception e) {
            e.printStackTrace();
            return Result.builder().type(Result.Type.BUCKET).state(Result.State.FAILED).exception(e).build();
        }
    }

    /**
     * 根据 bucket name 获取指定的bucket
     *
     * @param bucketName bucket 名称
     * @return Bucket
     */
    public Result get(String bucketName) {
        try {
            Bucket bucket = minioClient.listBuckets().stream().filter(item -> item.name().equals(bucketName)).findFirst().orElse(null);
            return Result.builder().bucket(bucket).type(Result.Type.BUCKET).state(Result.State.SUCCESS).build();
        } catch (Exception e) {
            e.printStackTrace();
            return Result.builder().type(Result.Type.BUCKET).state(Result.State.FAILED).exception(e).build();
        }
    }

    /**
     * 移除给定bucket name的bucket
     *
     * @param bucketName bucket name
     * @result Result
     */
    public Result remove(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
            return Result.builder().type(Result.Type.BUCKET).state(Result.State.SUCCESS).build();
        } catch (Exception e) {
            e.printStackTrace();
            return Result.builder().exception(e).build();
        }
    }

    /**
     * 获取给定bucket中的object
     *
     * @param bucketName bucket name
     * @param objectName object name
     * @return Result
     */
    public Result getObject(String bucketName, String objectName) {
        try {
            GetObjectResponse response = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
            return Result.builder().type(Result.Type.OBJECT).state(Result.State.SUCCESS).ins(response).build();
        } catch (Exception e) {
            return Result.builder().type(Result.Type.OBJECT).state(Result.State.FAILED).exception(e).build();
        }
    }

    /**
     * 获取对象的URL
     *
     * @param bucketName bucket name
     * @param objectName object name
     * @return
     */
    public Result getObjectUrl(Method method, String bucketName, String objectName, int expire, TimeUnit unit) {
        try {
            String url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(objectName).expiry(expire, unit).method(method).build());
            return Result.builder().url(url).type(Result.Type.OBJECT).state(Result.State.SUCCESS).build();
        } catch (Exception e) {
            e.printStackTrace();
            return Result.builder().type(Result.Type.OBJECT).state(Result.State.FAILED).exception(e).build();
        }
    }

    /**
     * @param bucketName  bucket name
     * @param objectName  object name
     * @param ins         文件流
     * @param size        文件大小
     * @param contentType 文件类型
     */
    public Result putObject(String bucketName, String objectName, InputStream ins, long size, String contentType) {
        try {
            Result exists = createIfNotExists(bucketName);
            if (exists.getException() != null) {
                return Result.builder().type(Result.Type.OBJECT).state(Result.State.FAILED).exception(exists.getException()).build();
            }
            minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(ins, size, -1).contentType(contentType).build());
            String url = String.format("%s/%s%s", properties.getEndpoint(), bucketName, objectName);
            return Result.builder().url(url).type(Result.Type.OBJECT).state(Result.State.SUCCESS).build();
        } catch (Exception e) {
            e.printStackTrace();
            return Result.builder().type(Result.Type.OBJECT).state(Result.State.FAILED).exception(e).build();
        }
    }

    /**
     * @param bucketName bucket name
     * @param objectName object name
     * @param ins        file input stream
     */
    public Result putObject(String bucketName, String objectName, InputStream ins) {
        try {
            String contentType = objectName.substring(objectName.lastIndexOf("."));
            int size = ins.available();
            return putObject(bucketName, objectName, ins, size, contentType);
        } catch (Exception e) {
            e.printStackTrace();
            return Result.builder().type(Result.Type.OBJECT).state(Result.State.FAILED).exception(e).build();
        }
    }

    /**
     * @param file
     * @param bucketName
     * @param objectName
     */
    public Result putObject(MultipartFile file, String bucketName, String objectName) {
        if (file == null) {
            logger.error("file can not be null");
            return Result.builder().type(Result.Type.OBJECT).state(Result.State.FAILED).exception(new Exception("file can not be null")).build();
        }
        if (objectName == null) {
            objectName = file.getOriginalFilename();
        }
        try {
            InputStream ins = file.getInputStream();
            long size = file.getSize();
            String contentType = file.getContentType();
            return putObject(bucketName, objectName, ins, size, contentType);
        } catch (IOException e) {
            e.printStackTrace();
            return Result.builder().type(Result.Type.OBJECT).state(Result.State.FAILED).exception(e).build();
        }

    }

    /**
     * 删除给定的 bucket的object
     *
     * @param bucketName bucket name
     * @param objectName object name
     */
    public Result removeObject(String bucketName, String objectName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
            return Result.builder().type(Result.Type.OBJECT).state(Result.State.SUCCESS).build();
        } catch (Exception e) {
            e.printStackTrace();
            return Result.builder().type(Result.Type.OBJECT).state(Result.State.FAILED).exception(e).build();
        }
    }


}

  • 封装操作结果 Result
package com.geekyous.minio.bucket;

import io.minio.messages.Bucket;

import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.List;

/**
 *  bucket 操作结果
 **/
public class Result {
    private final String url;
    private final Type type;
    private final State state;

    private final InputStream ins;

    private final Bucket bucket;

    private final List<Bucket> buckets;

    private final Exception exception;

    private final LocalDateTime updateTime;

    private Result(Builder builder) {
        this.url = builder.url;
        this.type = builder.type;
        this.state = builder.state;
        this.bucket = builder.bucket;
        this.buckets = builder.buckets;
        this.ins = builder.ins;
        this.exception = builder.exception;
        this.updateTime = builder.updateTime;
    }

    public String getUrl() {
        return url;
    }

    public Type getType() {
        return type;
    }

    public State getState() {
        return state;
    }

    public InputStream getIns() {
        return ins;
    }

    public Bucket getBucket() {
        return bucket;
    }

    public List<Bucket> getBuckets() {
        return buckets;
    }

    public Exception getException() {
        return exception;
    }

    public LocalDateTime getUpdateTime() {
        return updateTime;
    }

    public static Builder builder() {
        return new Builder();
    }

    protected static class Builder {
        private String url;
        private Type type;
        private State state;
        private InputStream ins;
        private Bucket bucket;
        private List<Bucket> buckets;
        private Exception exception;

        private LocalDateTime updateTime = LocalDateTime.now();

        public Builder url(String url) {
            this.url = url;
            return this;
        }

        public Builder type(Type type) {
            this.type = type;
            return this;
        }

        public Builder state(State state) {
            this.state = state;
            return this;
        }

        public Builder updateTime(LocalDateTime updateTime) {
            this.updateTime = updateTime;
            return this;
        }

        public Builder bucket(Bucket bucket) {
            this.bucket = bucket;
            return this;
        }

        public Builder buckets(List<Bucket> buckets) {
            this.buckets = buckets;
            return this;
        }

        public Builder ins(InputStream ins) {
            this.ins = ins;
            return this;
        }

        public Builder exception(Exception exception) {
            this.exception = exception;
            return this;
        }

        public Result build() {
            if (this.state == State.FAILED && this.exception == null) {
                throw new RuntimeException("exception can not be null when state is failed");
            }
            return new Result(this);
        }
    }

    enum State {
        SUCCESS,
        FAILED,
    }

    enum Type {
        BUCKET,
        OBJECT
    }

    @Override
    public String toString() {
        return "Result{" +
                "url='" + url + '\'' +
                ", type=" + type +
                ", state=" + state +
                ", ins=" + ins +
                ", bucket=" + bucket +
                ", buckets=" + buckets +
                ", exception=" + exception +
                ", updateTime=" + updateTime +
                '}';
    }
}

  • BucketOperatorTest 测试类
package com.geekyous.minio.bucket;

import io.minio.http.Method;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import java.io.*;
import java.util.concurrent.TimeUnit;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
class BucketOperatorTest {
    private final static Logger logger = LoggerFactory.getLogger(BucketOperatorTest.class);
    @Autowired
    private BucketOperator bucketOperator;

    @Test
    void create() {
        Result bucket = bucketOperator.create("bucket-test");
        assertNull(bucket.getException());
    }

    @Test
    void createIfNotExists() {
        Result result = bucketOperator.create("bucket-test");
        assertNotNull(result.getException());
        assertEquals(result.getState(), Result.State.FAILED);
    }

    @Test
    void getAll() {
        Result result = bucketOperator.getAll();
        assertNull(result.getException());
        assertEquals(result.getState(), Result.State.SUCCESS);
        assertTrue(result.getBuckets().stream().anyMatch(i -> i.name().equals("bucket-test")));
    }

    @Test
    void get() {
        Result result = bucketOperator.get("bucket-test");
        assertNull(result.getException());
        assertEquals(result.getState(), Result.State.SUCCESS);
        assertEquals(result.getBucket().name(), "bucket-test");
    }

    @Test
    void remove() {
        Result result = bucketOperator.remove("bucket-test");
        assertEquals(result.getState(), Result.State.SUCCESS);
        assertNull(result.getException());
    }

    @Test
    void putObject() {
        try {
            Resource resource = new ClassPathResource("object-test.txt");
            InputStream ins = resource.getInputStream();
            Result result = bucketOperator.putObject("bucket-test", "object.txt", ins);
            assertNull(result.getException());
            assertEquals(result.getState(), Result.State.SUCCESS);
            assertNotNull(result.getUrl());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Test
    void getObject() {
        try {
            Result result = bucketOperator.getObject("bucket-test", "object.txt");
            assertEquals(result.getState(), Result.State.SUCCESS);
            assertNull(result.getException());
            FileOutputStream fos = null;
            fos = new FileOutputStream("./object1.txt");
            byte[] b = new byte[1024];
            while ((result.getIns().read(b)) != -1) {
                fos.write(b);
            }
            result.getIns().close();
            fos.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Test
    void getObjectUrl() {
        Result result = bucketOperator.getObjectUrl(Method.GET, "bucket-test", "object.txt", 1, TimeUnit.HOURS);
        assertNull(result.getException());
        assertEquals(result.getState(), Result.State.SUCCESS);
        logger.info(result.getUrl());
    }

    @Test
    void removeObject() {
        Result result = bucketOperator.removeObject("bucket-test", "object.txt");
        assertEquals(result.getState(), Result.State.SUCCESS);
        assertNull(result.getException());
    }
}

  • 11
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我理解了你的问题。如果你想在 SpringBoot 应用使用 Minio 作为云存储服务,可以按照以下步骤进行操作: 1. 添加 Minio 客户端依赖 在 `pom.xml` 文件添加 Minio 客户端依赖: ```xml <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>8.2.1</version> </dependency> ``` 2. 配置 Minio 客户端 在 `application.properties` 文件添加以下配置: ```properties minio.url=http://localhost:9000 minio.access-key=minio minio.secret-key=minio123 minio.bucket-name=my-bucket ``` 其,`minio.url` 是 Minio 服务的访问地址,`minio.access-key` 和 `minio.secret-key` 是访问 Minio 服务的凭证,`minio.bucket-name` 是存储对象的桶名称。 3. 实现上传文件接口 ```java @RestController public class FileController { @Autowired private MinioClient minioClient; @PostMapping("/files") public String uploadFile(@RequestParam("file") MultipartFile file) throws Exception { String fileName = file.getOriginalFilename(); InputStream inputStream = file.getInputStream(); long size = file.getSize(); String contentType = file.getContentType(); minioClient.putObject(PutObjectArgs.builder() .bucket("my-bucket") .object(fileName) .stream(inputStream, size, -1) .contentType(contentType) .build()); return "File uploaded successfully: " + fileName; } } ``` 4. 实现展示图片接口 ```java @RestController public class ImageController { @Autowired private MinioClient minioClient; @GetMapping("/images/{fileName}") public ResponseEntity<byte[]> getImage(@PathVariable String fileName) throws Exception { InputStream inputStream = minioClient.getObject(GetObjectArgs.builder() .bucket("my-bucket") .object(fileName) .build()); byte[] bytes = IOUtils.toByteArray(inputStream); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.IMAGE_JPEG); headers.setContentLength(bytes.length); return new ResponseEntity<>(bytes, headers, HttpStatus.OK); } } ``` 在这个示例,我们定义了一个 `FileController` 和一个 `ImageController`,分别用于上传文件和展示图片。其,`ImageController` 的 `getImage` 方法根据文件名从 Minio 服务获取图片,并返回 `ResponseEntity<byte[]>` 对象,该对象包含图片的二进制数据和响应头信息,用于在浏览器展示图片。 以上就是在 SpringBoot 应用使用 Minio 作为云存储服务的步骤。希望能对你有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值