课堂目标

  1. 了解什么是Minio
  2. 下载与安装
  3. SpringBoot集成

Minio使用

一、Minio是什么?

image-20251016202802900

1.1 Minio 介绍(文件存储服务器)

https://minio.org.cn

MinIO 是一个基于 Apache License v2.0 开源协议的对象存储服务。它兼容亚马逊 AWS S3 云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器 / 虚拟机镜像等,而一个对象文件可以是任意大小,从几 kb 到最大 5T 不等。
MinIO 是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或者 MySQL。

AWS S3(Amazon Simple Storage Service)本身是一项对象存储服务,而它定义的API接口规范,已成为业界事实标准

概念:S3协议本质上是一套基于HTTP/HTTPS的RESTful API接口规范,定义了如何通过HTTP请求来操作云端数据。

S3 的核心概念

  • Bucket(存储桶):对象的容器,全局唯一。存储文件对象的目录。
  • Object(对象):存储的基本数据单元,由数据、Key(键) 和元数据组成。Key是对象的唯一标识。
  • Endpoint(访问端点):服务的访问地址;URL (c://data/file....)

二、minio安装

1、docker安装(可选)

https://minio.org.cn/solutions/modern-data-lakes-lakehouses.html

  • docker下载mimio
docker pull minio/minio
  • 在/data目录下创建minio的配置文件和数据文件,方便把容器内部的文件映射到宿主机
cd /data
mkdir -p minio/config
mkdir -p minio/data
  • 拉起镜像
docker run \                 #运行或者叫拉起
	--name minio \           #别名
	-p 9000:9000  \          #映射端口 ,前面是宿主机 后面是容器内部
	-p 9090:9090  \          
	-d \                     #后台启动
	-e "MINIO_ROOT_USER=minio" \             # 设置一个环境变量值 用户名
	-e "MINIO_ROOT_PASSWORD=minio123" \       # 密码
	-v /data/minio/data:/data \              #映射  把容器的 /data目录 映射到 宿主机的/data/minio/data
	-v /data/minio/config:/root/.minio \     #配置映射
	minio/minio server /data --console-address ":9090" --address ":9000"  
	#minio/minio minio的命令  server 服务启动  /data指定文件存放的目录   --consol-address代表web界面的端口  --addresss 9000 代表是上传的端口
docker run \   
--name minio \
-p 9000:9000  \
-p 9090:9090  \
-d \
-e "MINIO_ROOT_USER=minio" \
-e "MINIO_ROOT_PASSWORD=minio123" \
-v /data/minio/data:/data \
-v /data/minio/config:/root/.minio \
minio/minio server /data --console-address ":9090" --address ":9000"
docker run --name minio -p 9000:9000 -p 9090:9090 -d -e "MINIO_ROOT_USER=minio" -e "MINIO_ROOT_PASSWORD=minio123" -v /data/minio/data:/data -v /data/minio/config:/root/.minio minio/minio server /data --console-address ":9090" --address ":9000"

2、windows安装(必会)

  • 下载

image-20260510121524157

  • 配置目录+密码

    如果不在环境变量里面配置密码,会给一个默认的密码;
    在环境变量里面配置参数(用户名和密码),是为了安全性,密码不会被轻易看到,一般只有拿到找个主机权限的人才会知道密码
    PS> set MINIO_ROOT_USER=admin 
    PS> set MINIO_ROOT_PASSWORD=minioadmin123
    PS> C:\minio.exe server F:\Data --console-address ":9001"   //启动
    

    1、自己创建一个目录data

    2、设置环境变量密码

    3、minio.exe就是启动文件服务的执行文件,后面的server代表提供文件服务 ,后面的是存放的目录,--console代表启动的端口

image-20260510122443792

访问控制台

三、控制台看一下

  • 登录
http://localhost:9001/login
minio
minio123

image-20251016163017920

  • 新建一个bucket,必须要有

image-20251016174936890

image-20251016175249703

image-20251016175326615

image-20251016175403692

image-20251016175422562

四、springboot如何使用

  • 加依赖
	<!--minio-->
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>8.5.10</version>
        </dependency>
      
	<!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>
        <!-- 工具包 util -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.3.10</version>
        </dependency>
  • 配置、
minio:
  access-key: minio
  secret-key: minio123
  endpoint: http://47.109.102.26:9000
  bucket: woniu
  • 配置类
package com.dw.config;

import io.minio.MinioClient;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "minio")
@Data
public class MinioConfig {
    private String endpoint;
    private String accessKey;
    private String secretKey;
    private String bucket;

    @Bean
    public MinioClient minioClient() {
        return MinioClient.builder()
                .endpoint(endpoint)
                .credentials(accessKey, secretKey)
                .build();
    }
}
  • 编写service
package com.dw.service;

import io.minio.errors.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

/**
 * @author daiwei
 * @date 2025/10/16/19:33
 * @Description:
 */
public interface MinioService {

    String uploadFile(MultipartFile file) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException;

    String getFileUrl(String fileName, int expires);
}

  • 编写service实现
package com.dw.service.impl;

import cn.hutool.core.date.DateUtil;
import com.dw.config.MinioConfig;
import com.dw.service.MinioService;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author daiwei
 * @date 2025/10/16/19:33
 * @Description:
 */
@Service
public class MinioServiceImpl implements MinioService {
    @Autowired
    MinioConfig minioConfig;

    public String uploadFile(MultipartFile file) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        // 1. 检查存储桶是否存在,不存在则创建
        if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(minioConfig.getBucket()).build())) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(minioConfig.getBucket()).build());
        }

        // 2. 生成唯一文件名
        String originalFilename = file.getOriginalFilename(); // example.txt
        String fileExtension = originalFilename.substring(originalFilename.lastIndexOf(".")); // .txt
        String objectName = DateUtil.today().replace("-", "")+"/"+ UUID.randomUUID().toString() + fileExtension; // 随机字符串 + .txt

        // 3. 上传文件
        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(minioConfig.getBucket()) // 存储桶名称
                        .object(objectName) // 对象名称(文件名,可以包含目录,test/随机字符串.txt)
                        .stream(file.getInputStream(), file.getSize(), -1) // 文件流,文件大小,每次读取
                        .contentType(file.getContentType()) // 文件类型,比如 image/jpeg
                        .build());

        return objectName;
    }

    /**
     * @param objectFile 对象文件
     * @return {@link String }
     * @Description 根据文件名和桶获取文件路径
     */
    @Override
    public String getFileUrl(String objectFile,int expires) {
        try {

            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(minioConfig.getBucket())
                    .object(DateUtil.today().replace("-", "")+"/"+objectFile)
                    .expiry(expires, TimeUnit.SECONDS)
                    .build()
            );
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

  • controller
package com.dw.controller;

import com.dw.service.MinioService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.Arrays;
import java.util.List;

/**
 * @author daiwei
 * @date 2025/10/16/19:56
 * @Description:
 */
@RestController
@RequestMapping("/minio")
public class MinioController{
    // 最大文件大小(10MB)
    private static final long MAX_FILE_SIZE = 10 * 1024 * 1024;

    // 允许的文件类型
    private static final List<String> ALLOWED_EXTENSIONS = Arrays.asList(
            ".jpg", ".jpeg", ".png", ".gif", ".pdf", ".doc", ".docx", ".txt"
    );
    @Autowired
    MinioService orderService;

    @PostMapping("/upload")
    public String upload(@RequestParam("file") MultipartFile file) throws Exception {
        if(file.getSize()>MAX_FILE_SIZE){ 
            throw new IllegalAccessException("文件过大");
        }
        String ext = file.getOriginalFilename().substring( file.getOriginalFilename().lastIndexOf("."));
        if (!ALLOWED_EXTENSIONS.contains(ext)) {
            throw new IllegalArgumentException("不支持的文件类型,仅支持: " + ALLOWED_EXTENSIONS);
        }
        return orderService.uploadFile(file);
    }

    @GetMapping("/getUrl")
    public String getFileUrl(@RequestParam("filename") String fileName){
        return orderService.getFileUrl(fileName, 30);
    }
}

五、还有哪些API

image-20251016200315820

image-20251016202513691

六、其他介绍(可选)

官网api demo

https://github.com/minio/minio-java/blob/release/examples/UploadObject.java

  • 分片上传

把一个大文件,拆分成n个小文件,逐一上传;

上传完毕,后把N个文件最终合并成一个整体文件。

分片:

1、随时可以暂停,更灵活

2、断点续传

3、多线程去上传

image-20251017111746551

  • 秒传(闪传)

    用hash或者是md5 标志唯一的指纹,在下一次上传的时候,再次用同样的hash或者md5去验证是否一样

    如果是一样就代表已经被上传过,只需要做对应的md5值关联文件对象即可。

image-20251017112235357