Minio快速入门

文章目录


一、文件存储方案

 在现代应用开发中,文件(图片、视频、文档、日志等)的存储和管理是一个基础需求。常见的文件存储方案有以下几种:

  • 本地文件存储:直接将文件保存在应用服务器的本地磁盘(如 ./uploads/ 目录)。实现简单、无额外依赖、读写速度快,但不易扩展
  • 网络附属存储(NAS / 共享文件系统):使用 NFS、SMB/CIFS 等协议,将文件存储在专用的网络存储设备上,多台应用服务器共享同一目录。解决了多服务器共享文件的问题;容量可扩展;但性能受网络和 NAS 设备限制,仍存在中心化瓶颈,配置和维护相对复杂。
  • 云对象存储(OSS / S3):使用云厂商提供的对象存储服务(如 AWS S3、阿里云 OSS、腾讯云 COS、华为云 OBS 等),通过 HTTP REST API 存取文件。可以无限容量,按量付费,具有高可用、高持久性;但依赖公网或专线,每次读写有网络延迟和费用
  • 自建对象存储(MinIO、Ceph RGW、Swift):在私有云或本地数据中心部署对象存储系统,提供 S3 兼容的 API。数据完全自主可控,安全合规,且无厂商锁定,可部署在任意 Linux/Windows 服务器或 Kubernetes 上,但需要自行运维,有硬件投资和机房环境要求。

二、MinIO 简介

 MinIO 是一个 高性能、Kubernetes 原生、完全兼容 Amazon S3 API 的对象存储服务器。

  • 采用 Go 语言编写,单一二进制文件即可启动,支持 Windows、Linux、macOS 以及 Docker/Kubernetes 环境。
  • MinIO 的设计目标是为云原生工作负载提供轻量、快速、可扩展的对象存储,既可以单机运行(用于开发/测试),也可以部署成分布式集群(用于生产环境,实现数据冗余和高可用)。
  • 由于其 S3 兼容性,任何为 AWS S3 编写的 SDK(Java、Python、Go、Node.js 等)和工具(如 awscli、s3cmd、rclone)都可以无缝对接 MinIO。

1.核心特性

  • S3 兼容:完整实现 Amazon S3 API,包括 Bucket、Object、Multipart Upload、Presigned URL、Bucket Policy 等
  • 高性能:在合适硬件上可达到 每秒数百 GB 的读写吞吐;利用 SIMD 指令集加速纠删码(Erasure Coding)计算
  • 轻量 & 简单:单个二进制文件(约 100MB),无外部依赖(不依赖数据库),配置简单(环境变量或命令行参数)
  • 分布式支持:支持分布式模式(多个磁盘/多台服务器),使用纠删码提供数据冗余和自动修复,无需 RAID
  • 云原生友好:提供 Kubernetes Operator,可轻松在 K8s 上部署;支持 Prometheus 监控指标;与 CSI(容器存储接口)集成
  • 安全:支持 TLS/HTTPS、服务器端加密(SSE-S3、SSE-C、SSE-KMS)、IAM 身份管理、Bucket 策略和临时凭证(STS)
  • 生命周期管理:支持对象过期、转换到冷存储层(需配合其他系统)
  • 事件通知:支持将 Bucket 事件(上传、删除等)发送到 Kafka、AMQP、Redis、Webhook 等目标
  • 多租户:通过多个 MinIO 实例或 Bucket 策略实现租户隔离

2.适用场景

  • 开发与测试环境:在本地或 CI/CD 管道中快速搭建一个 S3 兼容的存储,避免连接真实云服务带来的网络延迟和费用。
  • 私有云/企业内部文件存储:替代传统的文件服务器(NFS/SMB),为内部应用提供统一的对象存储接口,支持海量小文件或大文件的稳定存储。
  • 大数据与 AI 训练:作为数据湖的存储底座,存储日志、Parquet/ORC 格式数据,供 Spark、Presto、TensorFlow 等计算框架直接读取。MinIO 对 Hadoop S3A 有良好支持。
  • 容器化应用的持久化存储:在 Kubernetes 中作为 PVC 后端(通过 CSI 或 S3 兼容的 FUSE 工具),为无状态应用提供持久卷。
  • 备份与归档:使用 rclone、Velero、Dell EMC Networker 等备份工具将数据直接备份到 MinIO,并通过生命周期管理转储至冷存储层。
  • 边缘计算 / IoT:由于单二进制部署极其轻量,可以运行在树莓派、边缘网关等设备上,收集并存储传感器数据,再通过同步功能汇总到中心云。
  • 混合云架构中的缓存层:在本地部署 MinIO 作为高速缓存,通过异步复制将数据最终同步到公有云对象存储,平衡访问延迟和成本。

三、访问控制台与基本操作

下载安装参考连接

1.Web端基础操作

 可以登录 Web 控制台创建存储桶(Bucket),注意创建桶名时必须介于 3 到 63 个字符之间;桶名称只能由小写字母、数字、句点 (.) 和连字符 (-) 组成;桶名称必须以字母或数字开头和结尾;且名称不得采用 IP 地址格式,否则会报错。

 可以上传、下载、删除文件

2.共享访问链接

 ①可以选中文件生成共享访问链接,但是这种方式生成的链接,有效期最长只能设置到12小时,无法延长。


 ②可以使用 mc 命令行客户端生成永久共享链接(推荐),下载 mc 并配置连接别名

bash 复制代码
mc alias set myminio http://127.0.0.1:9000 YOUR_USERNAME YOUR_PASSWORD

 将整个存储桶设为公开只读,即任何人都可以列出并下载桶内的所有文件(即永久共享)。

bash 复制代码
mc anonymous set download myminio/my-bucket-name

 ③也可以通过SDK编程实现永久访问配置,MinIO 所有官方 SDK 都兼容 AWS S3 的 Bucket Policy。以下以 Java 和 Python 为例,演示如何将存储桶设置为公开只读(永久共享)。

 Java代码如下

java 复制代码
import io.minio.*;
import io.minio.messages.*;

public class MakeBucketPublic {
    public static void main(String[] args) throws Exception {
        MinioClient minioClient = MinioClient.builder()
                .endpoint("http://127.0.0.1:9000")
                .credentials("YOUR_USERNAME", "YOUR_PASSWORD")
                .build();

        // 设置 bucket 策略为 "public-read"(允许所有人下载)
        String policy = "{\n" +
                "  \"Version\": \"2012-10-17\",\n" +
                "  \"Statement\": [\n" +
                "    {\n" +
                "      \"Effect\": \"Allow\",\n" +
                "      \"Principal\": \"*\",\n" +
                "      \"Action\": [\"s3:GetObject\"],\n" +
                "      \"Resource\": [\"arn:aws:s3:::my-bucket-name/*\"]\n" +
                "    }\n" +
                "  ]\n" +
                "}";
        minioClient.setBucketPolicy(SetBucketPolicyArgs.builder()
                .bucket("my-bucket-name")
                .config(policy)
                .build());

        System.out.println("Bucket is now public. Permanent URL: http://127.0.0.1:9000/my-bucket-name/your-file.jpg");
    }
}

 Python 示例如下

py 复制代码
from minio import Minio

client = Minio(
    "127.0.0.1:9000",
    access_key="YOUR_USERNAME",
    secret_key="YOUR_PASSWORD",
    secure=False
)

policy = {
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": ["s3:GetObject"],
            "Resource": ["arn:aws:s3:::my-bucket-name/*"]
        }
    ]
}

client.set_bucket_policy("my-bucket-name", json.dumps(policy))
print("永久共享链接格式:http://127.0.0.1:9000/my-bucket-name/文件名")

 ④如果你不希望将整个桶公开,但又需要生成一个有效期很长(例如 1 年) 的分享链接,可以通过 SDK 生成自定义有效期的预签名 URL。不过请注意,MinIO 官方建议最长不超过 7 天,但通过代码可以设置更大的值(如 365 天)。示例(Java):

java 复制代码
String url = minioClient.getPresignedObjectUrl(
    GetPresignedObjectUrlArgs.builder()
        .bucket("my-bucket-name")
        .object("my-file.pdf")
        .method(Method.GET)
        .expiry(3600 * 24 * 365)  // 365 天,单位秒
        .build()
);
System.out.println("Long-term URL: " + url);

四、客户端工具(mc)简介

 MinIO 官方命令行客户端 mc(MinIO Client)提供了类似 UNIX 命令(如 ls、cp、mb 等),用于管理对象存储和兼容 S3 的服务。与直接操作文件系统不同,mc 通过 S3 API 与服务端交互,确保元数据一致性。

1.mc 的安装与配置

  • Windows :下载 mc.exe,放入任意目录。

  • macOS:brew install minio/stable/mc

  • Linux :

    bash 复制代码
    wget https://dl.min.io/client/mc/release/linux-amd64/mc
    chmod +x mc
    sudo mv mc /usr/local/bin/
  • Docker:docker pull minio/mc

配置别名(Alias)

bash 复制代码
mc alias set myminio http://127.0.0.1:9000 YOUR_ACCESS_KEY YOUR_SECRET_KEY

注意端口必须为 API 端口(默认 9000),不是 Web 控制台端口。

2.常用 mc 命令(ls、mb、cp、rm、policy)

命令 说明 示例
ls 列出存储桶或对象 mc ls myminio mc ls myminio/mybucket
mb 创建存储桶 mc mb myminio/new-bucket
cp 复制(上传/下载) 上传:mc cp ./file myminio/mybucket/ 下载:mc cp myminio/mybucket/file ./
rm 删除对象 mc rm myminio/mybucket/file.txt mc rm --force myminio/mybucket/
policy/anonymous 设置桶的匿名访问策略 mc anonymous set download myminio/mybucket(公开只读)

五、Java 快速集成示例

1.添加 MinIO Java SDK 依赖(Maven/Gradle)

 在项目中引入 MinIO Java SDK 的 Maven 依赖,当前官方推荐的稳定版本为 8.6.0(建议使用最新稳定版)。部分场景下,SDK 内部依赖 HTTP 客户端,如项目中缺少相关依赖,可一并添加 Apache HttpClient5:

xml 复制代码
<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>8.6.0</version>
</dependency>

<!-- 可选:Apache HttpClient5,部分场景可能依赖 -->
<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <version>5.3</version>
</dependency>

 若使用 Gradle

gradle 复制代码
dependencies {
    implementation("io.minio:minio:8.6.0")
}

提示:引入依赖后,MinioClient 对象建议在项目中作为单例使用,不要每次操作都重新创建客户端实例。

2.连接 MinIO 服务器

 所有操作都通过 MinioClient 对象执行,初始化时需传入 MinIO 服务的核心配置:服务地址、访问密钥和秘密密钥。MinioClient 使用建造者模式进行实例化,支持灵活配置 endpoint、region 及 HTTP 客户端设置。

java 复制代码
import io.minio.MinioClient;
import io.minio.errors.MinioException;

public class MinioConfig {
    // 配置参数(可根据需要提取到配置文件)
    private static final String MINIO_ENDPOINT = "http://127.0.0.1:9000";
    private static final String MINIO_ACCESS_KEY = "your_access_key";
    private static final String MINIO_SECRET_KEY = "your_secret_key";

    /**
     * 获取 MinIO 客户端实例(单例模式)
     */
    public static MinioClient getMinioClient() {
        try {
            MinioClient client = MinioClient.builder()
                    .endpoint(MINIO_ENDPOINT)
                    .credentials(MINIO_ACCESS_KEY, MINIO_SECRET_KEY)
                    .build();
            // 验证客户端可用性
            client.listBuckets();
            return client;
        } catch (Exception e) {
            throw new RuntimeException("MinIO客户端初始化失败:" + e.getMessage(), e);
        }
    }
}
  • endpoint:MinIO 服务地址(API 端口,如 http://127.0.0.1:9000)
  • accessKey:访问密钥(即登录用户名)
  • secretKey:秘密密钥(即登录密码)

 在 Spring Boot 项目中集成,推荐将连接参数配置到 application.yml 中,通过 @ConfigurationProperties 读取并初始化客户端 Bean,方便后续维护和依赖注入。

yml 复制代码
# application.yml
minio:
  endpoint: http://127.0.0.1:9000
  access-key: your_access_key
  secret-key: your_secret_key
java 复制代码
import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MinioConfig {

    @Value("${minio.endpoint}")
    private String endpoint;

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

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

    @Bean
    public MinioClient minioClient() {
        return MinioClient.builder()
                .endpoint(endpoint)
                .credentials(accessKey, secretKey)
                .build();
    }
}

3.创建存储桶、上传文件

 MinIO 将存储桶(Bucket)作为顶层容器,所有文件都存放在存储桶中。在上传文件之前,最好先检查目标存储桶是否存在,若不存在则创建。

 判断存储桶是否存在并创建

java 复制代码
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.UploadObjectArgs;

public class MinioFileService {
    private final MinioClient minioClient;

    public MinioFileService(MinioClient minioClient) {
        this.minioClient = minioClient;
    }

    /**
     * 确保存储桶存在,若不存在则创建
     */
    public void ensureBucketExists(String bucketName) throws Exception {
        boolean exists = minioClient.bucketExists(
            BucketExistsArgs.builder().bucket(bucketName).build()
        );
        if (!exists) {
            minioClient.makeBucket(
                MakeBucketArgs.builder().bucket(bucketName).build()
            );
        }
    }
}

 上传文件(从本地文件系统)uploadObject 方法支持通过本地文件路径上传文件到 MinIO:

java 复制代码
import io.minio.UploadObjectArgs;

/**
 * 上传本地文件到 MinIO
 *
 * @param bucketName 存储桶名称
 * @param objectName 对象名称(即文件在桶中的路径,例如 "images/photo.jpg")
 * @param filePath   本地文件绝对路径
 */
public void uploadFile(String bucketName, String objectName, String filePath) throws Exception {
    ensureBucketExists(bucketName);

    minioClient.uploadObject(
        UploadObjectArgs.builder()
            .bucket(bucketName)
            .object(objectName)
            .filename(filePath)
            .build()
    );
}

提示:objectName 可以包含路径,例如 files/2025/photo.jpg,会在 MinIO 中模拟目录结构(实际上所有对象仍以扁平结构存储,只是按前缀组织)。

 上传文件(从 InputStream):若文件来自其他来源(如用户上传的 MultipartFile),可使用 putObject 方法(Java 中也提供了 putObject 方法用于上传对象):

java 复制代码
import io.minio.PutObjectArgs;
import java.io.InputStream;

/**
 * 从输入流上传文件到 MinIO
 */
public void uploadStream(String bucketName, String objectName, InputStream inputStream, 
                         String contentType, long size) throws Exception {
    ensureBucketExists(bucketName);

    minioClient.putObject(
        PutObjectArgs.builder()
            .bucket(bucketName)
            .object(objectName)
            .stream(inputStream, size, -1)
            .contentType(contentType)
            .build()
    );
}

4.获取文件 URL 或下载对象

  • 方式一:获取预签名 URL(带有效期):通过 getPresignedObjectUrl 方法可生成一个带有签名和有效期的临时下载链接,适用于为外部用户提供有时间限制的安全访问。MinIO Web 控制台生成的分享链接有效期最长 12 小时,而通过 SDK 可以自定义过期时间
java 复制代码
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.http.Method;
import java.util.concurrent.TimeUnit;

/**
 * 获取预签名 URL(适用于私有桶的文件临时访问)
 *
 * @param bucketName 存储桶名称
 * @param objectName 对象名称
 * @param expiry     过期时长(单位:秒)
 * @return 预签名 URL(超期后自动失效)
 */
public String getPresignedUrl(String bucketName, String objectName, int expirySeconds) throws Exception {
    return minioClient.getPresignedObjectUrl(
        GetPresignedObjectUrlArgs.builder()
            .method(Method.GET)
            .bucket(bucketName)
            .object(objectName)
            .expiry(expirySeconds)
            .build()
    );
}

// 使用示例:生成一个 7 天有效的下载链接
String url = getPresignedUrl("my-bucket", "myfile.jpg", (int) TimeUnit.DAYS.toSeconds(7));
  • 方式二:直接下载文件到本地:当需要在后端读取文件内容时(例如 Java 程序需要处理文件数据),直接下载比使用预签名 URL 更合适
java 复制代码
import io.minio.DownloadObjectArgs;

/**
 * 从 MinIO 下载文件到本地
 */
public void downloadFile(String bucketName, String objectName, String destinationPath) throws Exception {
    minioClient.downloadObject(
        DownloadObjectArgs.builder()
            .bucket(bucketName)
            .object(objectName)
            .filename(destinationPath)
            .build()
    );
}
  • 方式三:获取文件的 InputStream(浏览器下载):在 Web 应用中,可以将文件内容直接写入 HTTP 响应流,供用户下载或预览
java 复制代码
import io.minio.GetObjectArgs;
import jakarta.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * 获取文件输入流并返回给客户端
 */
public void downloadToResponse(String bucketName, String objectName, HttpServletResponse response) throws Exception {
    try (InputStream stream = minioClient.getObject(
            GetObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .build());
         OutputStream os = response.getOutputStream()) {

        // 设置响应头(根据文件类型调整 Content-Type)
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment; filename=\"" + objectName + "\"");

        byte[] buffer = new byte[8192];
        int bytesRead;
        while ((bytesRead = stream.read(buffer)) != -1) {
            os.write(buffer, 0, bytesRead);
        }
    }
}

说明:getObject 方法返回文件内容的 InputStream,可以灵活地处理(如转换格式、边读边处理等),比直接下载到本地文件更适用于内存处理场景。

 完整示例代码

java 复制代码
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.UploadObjectArgs;
import io.minio.DownloadObjectArgs;
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.http.Method;

public class MinIODemo {
    public static void main(String[] args) throws Exception {
        // 1. 初始化客户端
        MinioClient minioClient = MinioClient.builder()
                .endpoint("http://127.0.0.1:9000")
                .credentials("admin", "your_secure_password")
                .build();

        String bucketName = "my-demo-bucket";
        String objectName = "hello.txt";
        String sourceFile = "C:/temp/hello.txt";
        String destFile = "C:/temp/hello_downloaded.txt";

        // 2. 确保存储桶存在
        boolean found = minioClient.bucketExists(
            BucketExistsArgs.builder().bucket(bucketName).build()
        );
        if (!found) {
            minioClient.makeBucket(
                MakeBucketArgs.builder().bucket(bucketName).build()
            );
            System.out.println("Bucket created: " + bucketName);
        }

        // 3. 上传文件
        minioClient.uploadObject(
            UploadObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .filename(sourceFile)
                .build()
        );
        System.out.println("File uploaded: " + objectName);

        // 4. 生成预签名 URL
        String url = minioClient.getPresignedObjectUrl(
            GetPresignedObjectUrlArgs.builder()
                .method(Method.GET)
                .bucket(bucketName)
                .object(objectName)
                .expiry(7 * 24 * 3600)  // 7 days
                .build()
        );
        System.out.println("Presigned URL: " + url);

        // 5. 下载文件到本地
        minioClient.downloadObject(
            DownloadObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .filename(destFile)
                .build()
        );
        System.out.println("File downloaded to: " + destFile);
    }
}

 实际开发建议

  • 配置分离:将 endpoint、accessKey、secretKey 等参数提取到配置文件中,便于不同环境切换(开发/测试/生产),也避免将敏感信息硬编码在代码中。
  • 封装 Service 层:将 MinIO 操作封装成独立的 Service(如 MinioFileService),提供统一的上传、下载、删除等接口,配合 Spring 的 @Service 注解便于业务层调用。
  • 异常处理:MinIO SDK 的调用可能会抛出 MinioException、InvalidKeyException 等异常,建议在封装层统一处理并转换为业务异常。
相关推荐
驭渊的小故事19 分钟前
Spring AI Alibaba 实战:从maven依赖治理到声明式工具调用
java·人工智能·spring
Elastic 中国社区官方博客43 分钟前
我们如何通过 70 多个拉取请求,将 OpenTelemetry Java agent 迁移到 invokedynamic
java·大数据·开发语言·elasticsearch·搜索引擎·全文检索·es
tyn1881 小时前
记录本地安装nacos服务的方法及异常
java·开发语言
步行cgn1 小时前
Spring MVC 视图机制的核心接口与类
java·开发语言·后端·spring·mvc
y1su2 小时前
Leetcode 字符串模板
java·数据结构·算法·leetcode·职场和发展·动态规划
我命由我123453 小时前
Python 开发 - Python 的 pathlib
java·开发语言·后端·python·学习·java-ee·学习方法
苜蓿muxu3 小时前
Java 字符串拼接:+、StringBuilder、StringBuffer的区别
java
摇滚侠3 小时前
《Java并发编程之美》第 4 章 Java 并发包中原子操作类原理剖析 阅读笔记 7
java·笔记·python
旋律4144 小时前
同义方法区别(增加类与删除类)
java·后端