SpringBoot集成Minio(简单使用)

前言

1.什么是Minio?
MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。(我的理解就是对象存储服务。跟fastdfs很像,有服务端和客户端)

  1. docker安装Minio(别问我为什么用docker。两个字简单方便)
#拉取镜像
docker pull minio
#拉取镜像
docker run 
-p 9001:9000 -p 9090:9090  # 第一个端口是服务端口,第二个是客户端(9001是宿主机端口,9000是容器内部端口)
--net=host  #表示容器和宿主机共享一个ip
--name minio #容器名称
-d --restart=always  #自启动
-e "MINIO_ACCESS_KEY=minio"  #客户端账号(有点像Mysql的用户密码)
-e "MINIO_REGION=cn-north-1" 
-e "TZ=Asia/Beijing"  #设置容器时区(不设置会报错)
-e "MINIO_SECRET_KEY=minio123456" #客户端密码 (密码必须不得少于8位不然启动不了) 
-v /minio/data:/data  #挂载目录将/minio/data挂载到容器的/data目录,防止数据丢失
-v /minio/config:/root/.minio  minio/minio server /data 
--console-address ":9090" -address ":9001"

没有设置时区时会报错
error occurred
ErrorResponse(code = AccessDenied, message = Access denied, bucketName = bucket, objectName = null, resource = /bucket, requestId = null, hostId = null)
request={method=HEAD, url=http://192.168.186.128:9090/bucket, headers=Host: 192.168.186.128:9090
Accept-Encoding: identity
User-Agent: MinIO (amd64; amd64) minio-java/2.0.0
Content-MD5: 1B2M2Y8AsgTpgAmY7PhCfg==
x-amz-content-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-amz-date: 20220126T034536Z
Authorization: AWS4-HMAC-SHA256 Credential=REDACTED/20220126/us-east-1/s3/aws4_request, SignedHeaders=accept-encoding;content-md5;host;x-amz-content-sha256;x-amz-date, Signature=REDACTED
}
response={code=403, headers=Date: Wed, 26 Jan 2022 03:45:36 GMT
Content-Length: 209
Content-Type: text/xml; charset=utf-8
Connection: close
}
参考解决链接
2. 启动成功后访问http://ip:9090,进入控制台页面
在这里插入图片描述
用户密码就是上面启动命令里的账号密码
在这里插入图片描述
Buckets:我的理解就是一个打的文件盘,而下面的project和file是一个个文件夹,objects就是文件个数
3. SpringBoot简单使用

#pom文件
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- 操作minio的java客户端-->
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>8.5.2</version>
        </dependency>
        <!-- 操作minio的java客户端-->
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>8.2.1</version>
        </dependency>
        <!--        jwt鉴权相应依赖-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.11.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
#yml文件
server:
  port: 8080
spring:
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 10MB

  #minio配置
  minio:
    access-key: minio #启动命令里的账号
    secret-key: minio123456 #启动命令里的地址
    url: http://192.168.32.211:9001  #访问地址
    bucket-name: project
#配置文件
@Data
@Configuration
@ConfigurationProperties(prefix = "spring.minio")
public class MinioConfig {
    private String accessKey;

    private String secretKey;

    private String url;

    private String bucketName;

    @Bean
    public MinioClient minioClient(){
        return MinioClient.builder()
                .region("cn-north-1")
                .endpoint(url)
                .credentials(accessKey,secretKey)
                .build();
    }
}
#工具类
package com.example.producerserver.util;

import com.example.producerserver.config.MinioConfig;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import lombok.SneakyThrows;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;

import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
public class MinioUtils {

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private MinioConfig configuration;

    /**
     * @param name 名字
     * @return boolean
     * @Description description: 判断bucket是否存在,不存在则创建
     * @Author zy
     * @Date 2024/08/01
     */
    public boolean existBucket(String name) {
        boolean exists;
        try {
            exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build());
            if (!exists) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());
                exists = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            exists = false;
        }
        return exists;
    }

    /**
     * @param bucketName 存储bucket名称
     * @return {@link Boolean }
     * @Description 创建存储bucket
     * @Author zy
     * @Date 2024/08/01
     */
    public Boolean makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * @param bucketName 存储bucket名称
     * @return {@link Boolean }
     * @Description 删除存储bucket
     * @Author zy
     * @Date 2024/08/01
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * @param fileName 文件名称
     * @param time     时间
     * @return {@link Map }
     * @Description 获取上传临时签名
     * @Author zy
     * @Date 2024/08/01
     */
    @SneakyThrows
    public Map getPolicy(String fileName, ZonedDateTime time) {
        PostPolicy postPolicy = new PostPolicy(configuration.getBucketName(), time);
        postPolicy.addEqualsCondition("key", fileName);
        try {
            Map<String, String> map = minioClient.getPresignedPostFormData(postPolicy);
            HashMap<String, String> map1 = new HashMap<>();
            map.forEach((k, v) -> {
                map1.put(k.replaceAll("-", ""), v);
            });
            map1.put("host", configuration.getUrl() + "/" + configuration.getBucketName());
            return map1;
        } catch (ErrorResponseException e) {
            e.printStackTrace();
        } catch (InsufficientDataException e) {
            e.printStackTrace();
        } catch (InternalException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (InvalidResponseException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (ServerException e) {
            e.printStackTrace();
        } catch (XmlParserException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @param objectName 对象名称
     * @param method     方法
     * @param time       时间
     * @param timeUnit   时间单位
     * @return {@link String }
     * @Description 获取上传文件的url
     * @Author zy
     * @Date 2024/08/01
     */
    public String getPolicyUrl(String objectName, Method method, int time, TimeUnit timeUnit) {
        try {
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(method)
                    .bucket(configuration.getBucketName())
                    .object(objectName)
                    .expiry(time, timeUnit).build());
        } catch (ErrorResponseException e) {
            e.printStackTrace();
        } catch (InsufficientDataException e) {
            e.printStackTrace();
        } catch (InternalException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (InvalidResponseException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (XmlParserException e) {
            e.printStackTrace();
        } catch (ServerException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * @param file     文件
     * @param fileName 文件名称
     * @Description 上传文件
     * @Author zy
     * @Date 2024/08/01
     */
    public void upload(MultipartFile file, String fileName) {
        // 使用putObject上传一个文件到存储桶中。
        try {
            InputStream inputStream = file.getInputStream();
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(configuration.getBucketName())
                    .object(fileName)
                    .stream(inputStream, file.getSize(), -1)
                    .contentType(file.getContentType())
                    .build());
        } catch (ErrorResponseException e) {
            e.printStackTrace();
        } catch (InsufficientDataException e) {
            e.printStackTrace();
        } catch (InternalException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (InvalidResponseException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (ServerException e) {
            e.printStackTrace();
        } catch (XmlParserException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param objectName 对象名称
     * @param time       时间
     * @param timeUnit   时间单位
     * @return {@link String }
     * @Description 根据filename获取文件访问地址
     * @Author zy
     * @Date 2024/08/01
     */
    public String getUrl(String objectName, int time, TimeUnit timeUnit) {
        String url = null;
        try {
            url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(configuration.getBucketName())
                    .object(objectName)
                    .expiry(time, timeUnit).build());
        } catch (ErrorResponseException e) {
            e.printStackTrace();
        } catch (InsufficientDataException e) {
            e.printStackTrace();
        } catch (InternalException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (InvalidResponseException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (XmlParserException e) {
            e.printStackTrace();
        } catch (ServerException e) {
            e.printStackTrace();
        }
        return url;
    }

    /**
     * @param fileName
     * @return {@link ResponseEntity }<{@link byte[] }>
     * @Description description: 下载文件
     * @Author zy
     * @Date 2024/08/01
     */
    public ResponseEntity<byte[]> download(String fileName) {
        ResponseEntity<byte[]> responseEntity = null;
        InputStream in = null;
        ByteArrayOutputStream out = null;
        try {
            in = minioClient.getObject(GetObjectArgs.builder().bucket(configuration.getBucketName()).object(fileName).build());
            out = new ByteArrayOutputStream();
            IOUtils.copy(in, out);
            //封装返回值
            byte[] bytes = out.toByteArray();
            HttpHeaders headers = new HttpHeaders();
            try {
                headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            headers.setContentLength(bytes.length);
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setAccessControlExposeHeaders(Arrays.asList("*"));
            responseEntity = new ResponseEntity<byte[]>(bytes, headers, HttpStatus.BAD_REQUEST);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return responseEntity;
    }

    /**
     * @param objectFile 对象文件
     * @return {@link String }
     * @Description 根据文件名和桶获取文件路径
     * @Author zy
     * @Date 2024/08/01
     */
    public String getFileUrl(String objectFile) {
        try {

            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(configuration.getBucketName())
                    .object(objectFile)
                    .build()
            );
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }


}
#写个测试接口
package com.example.producerserver.controller;

import com.example.producerserver.util.MinioUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.HashMap;

@CrossOrigin
@RestController
@RequestMapping("/api")
@Log4j2
public class MinioFileUploadController {
    @Autowired
    private MinioUtils minioUtils;

    /**
     * @param file     文件
     * @param fileName 文件名称
     * @return {@link AjaxResult }
     * @Description 上传文件
     * @Author  zy
     * @Date 2024/08/01
     */
    @GetMapping("/upload")
    public Object uploadFile(@RequestParam("file") MultipartFile file, String fileName) {
        StopWatch stopWatch=new StopWatch();
        stopWatch.start("测试上传");
        minioUtils.upload(file, fileName);
        stopWatch.stop();
       log.info(stopWatch.prettyPrint());
        return"上传成功";

    }

    /**
     * @param fileName 文件名称
     * @return {@link ResponseEntity }
     * @Description dowload文件
     * @Author  zy
     * @Date 2024/08/01
     */
    @GetMapping("/dowload")
    public ResponseEntity dowloadFile(@RequestParam("fileName") String fileName) {
        return minioUtils.download(fileName);
    }

    /**
     * @param fileName 文件名称
     * @return {@link AjaxResult }
     * @Description 得到文件url
     * @Author  zy
     * @Date 2024/08/01
     */
    @GetMapping("/getUrl")
    public Object getFileUrl(@RequestParam("fileName") String fileName){
        HashMap map=new HashMap();
        map.put("FileUrl",minioUtils.getFileUrl(fileName));
        return map;
    }
}
  1. 测试
    在这里插入图片描述
    在这里插入图片描述当文件名相同时会覆盖掉之前的文件。
    在这里插入图片描述
    在这里插入图片描述
    上传2G文件耗时110秒左右,总的来说还行
  2. 总结:工作中用到的存储文件的方式是nas盘,最近由于文件越来越多,导致磁盘空间不可用,就想找个别的东西代替nas存储文件,偶然了解到minio分布式文件存储。本次demo只是简单介绍和使用,学习之路任到重远,欢迎指点
<think>嗯,用户想知道如何查询MinIO存储桶的总大小。首先,我需要回忆一下MinIO的相关知识。记得MinIO是一个对象存储服务,通常通过API或者命令行工具来管理。用户可能需要用命令行或者编程的方式来获取存储桶的大小。 首先想到的是MinIO的客户端工具mc。有没有相关的命令呢?对了,mc有一个stat命令,可能可以查看存储桶的信息。比如mc du命令,类似Linux的du,用来查看磁盘使用情况。可能需要指定存储桶的路径,比如mc du alias/bucketname,这样应该能显示存储桶的总大小和对象数量。不过得确保用户已经配置好了mc的别名,否则需要先配置访问密钥和端点。 另外,如果用户想通过API来实现,可能需要使用MinIOJava SDK或者其他语言的SDK。例如,Java中可以通过listObjects遍历存储桶中的所有对象,然后累加每个对象的大小。不过这种方法效率可能不高,特别是当存储桶中有大量对象时,遍历会比较慢。有没有更好的API方法呢?可能MinIO的Admin API中有相关的统计接口,比如AdminClient的getBucketUsage方法,这可以直接获取存储桶的使用情况,包括大小。不过需要确认Admin API的权限,用户可能需要有管理员权限才能调用。 还有用户提到的引用内容,引用[1]提到MinIO需要独占访问权限,所以用户需要确保在查询过程中不会干扰到MinIO的正常运行。引用[2]和[3]提到MinIO适合存储大容量数据,所以存储桶可能很大,查询方法需要高效。 另外,有没有其他方法?比如通过Prometheus监控集成,如果用户配置了Prometheus和Grafana,可能可以直接从监控仪表盘中查看存储桶的容量。不过这可能属于监控配置的一部分,不是直接查询的方法。 总结一下,可能的解决方案有: 1. 使用mc命令行工具的du命令。 2. 使用Admin API中的getBucketUsage方法(Java SDK示例)。 3. 遍历对象并累加大小(效率低,不推荐用于大存储桶)。 4. 通过监控系统查看。 需要根据用户的具体环境和权限来推荐合适的方法。如果用户有命令行访问权限,mc du是最简单的。如果是集成到应用中,Admin API更合适。同时要提醒用户权限问题,确保操作符合MinIO的独占访问要求,避免直接操作文件系统。</think>### 查询MinIO存储桶容量的方法 #### 1. 使用MinIO客户端工具(`mc`)的`du`命令 MinIO提供的客户端工具`mc`支持直接查看存储桶的容量和对象数量,操作步骤如下: 1. **安装并配置`mc`客户端** 确保已安装`mc`并配置了MinIO服务器的别名(例如`myminio`),配置命令示例: ```bash mc alias set myminio http://minio-server:9000 access_key secret_key ``` 2. **执行容量统计命令** 使用`mc du`命令指定存储桶路径,例如统计`mybucket`的容量: ```bash mc du myminio/mybucket ``` 输出结果类似: ``` 1.2 GiB myminio/mybucket (10 objects) ``` 此方法直接调用MinIO服务接口,无需底层文件系统操作,符合MinIO对存储卷的独占访问要求[^1]。 --- #### 2. 通过MinIO Admin API获取存储桶用量(Java SDK示例) 若需通过编程方式实现,可使用MinIO Java Admin API的`getBucketUsage`方法: ```java import io.minio.admin.MinioAdminClient; public class BucketSizeChecker { public static void main(String[] args) throws Exception { MinioAdminClient adminClient = MinioAdminClient.builder() .endpoint("http://minio-server:9000") .credentials("access_key", "secret_key") .build(); String bucketName = "mybucket"; Long usage = adminClient.getBucketUsage(bucketName); System.out.println("Bucket Size: " + usage + " bytes"); } } ``` 此方法通过Admin API直接获取存储桶的总大小,避免了遍历对象的性能问题[^3]。 --- #### 3. 遍历存储桶对象并累加大小(低效,仅限小规模数据) **不推荐用于生产环境**,但可作为验证手段: ```python from minio import Minio client = Minio("minio-server:9000", access_key="access_key", secret_key="secret_key") total_size = 0 objects = client.list_objects("mybucket", recursive=True) for obj in objects: total_size += obj.size print(f"Total bucket size: {total_size / 1024**3 :.2f} GiB") ``` --- ### 注意事项 - **权限要求**:Admin API需要管理员权限,普通用户需通过策略控制访问。 - **性能影响**:遍历对象的方法可能引发高延迟,建议优先使用`mc du`或Admin API[^2]。 - **数据一致性**:所有操作均通过MinIO接口完成,避免直接操作文件系统,确保数据完整性。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值