SpringBoot项目集成MinIO
最近在学习MinIO,所以想让自己的SpringBoot项目集成MinIO,在网上查阅资料,并进行操作的过程中遇到一些问题,所以想把自己遇到的坑和完成步骤记录下来供自己和各位查阅。
一. MinIO的下载安装以及基本使用
1. 下载地址:https://dl.min.io/server/minio/release/windows-amd64/minio.exe

2. 下载好后需要手动创建data文件夹用于存储MinIO中的数据。
3. 键入cmd
4. 设置MinIO的一些变量
set MINIO_ROOT_USER=admin
set MINIO_ROOT_PASSWORD=admin123
set MINIO_ACCESS_KEY=admin
set MINIO_SECRET_KEY=admin123
下面是我踩的坑,如果只设置了MINIO_ROOT_USER和MINIO_ROOT_PASSWORD的值,而不设置MINIO_ACCESS_KEY和MINIO_SECRET_KEY的值,当启动minio服务的时候就会报以下异常:

所以一定要设置好后面两个变量的值。
5. 启动minio服务
> minio.exeserver data

启动后就会出现minio服务的地址,按住ctrl再点击即可访问该网址。
6.进入登录页面后,输入对应的用户名和密码,也就是之前设置的MINIO_ROOT_USER和MINIO_ROOT_PASSWORD的值。
7. 进入主界面后,点击左侧导航栏中的Buckets,然后点击Create Bucket。


8. 创建好之后,点击左侧导航栏中的Object Browser就可以看到创建好的桶了。

9. 进入该桶,点击upload,上传一个文件,桶的默认权限是private,所以外界访问不到,需要修改访问权限为public,但是要注意安全问题。

10. 点击左侧导航栏中的Buckets,进入该桶,修改权限为public,这样外界就可以访问上传的文件了。

二. SpringBoot集成MinIO
- 引入依赖
2. 编写配置文件
server:
port: 8081
spring:
配置文件上传大小限制
servlet:
multipart:
max-file-size: 200MB
max-request-size: 200MB
minio:
host: http://127.0.0.1:9000
url: m i n i o . h o s t / {minio.host}/ minio.host/{minio.bucket}/
access-key: minioadmin
secret-key: minioadmin
bucket: public
3. 添加配置文件
package com.suwell.serv.ocr.component;
?
import io.minio.MinioClient;
import io.minio.ObjectStat;
import io.minio.PutObjectOptions;
import io.minio.Result;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.util.UriUtils;
?
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
?
?
@Component
public class MinioConfig implements InitializingBean {
?? ?@Value(value = "${minio.bucket}")? ?private String bucket;
?? ?@Value(value = "${minio.host}")? ?private String host;
?? ?@Value(value = "${minio.url}")? ?private String url;
?? ?@Value(value = "${minio.access-key}")? ?private String accessKey;
?? ?@Value(value = "${minio.secret-key}")? ?private String secretKey;
?? ?private MinioClient minioClient;
?? ?@Override? ?public void afterPropertiesSet() throws Exception {? ? ? ?Assert.hasText(url, "Minio url 为空");? ? ? ?Assert.hasText(accessKey, "Minio accessKey为空");? ? ? ?Assert.hasText(secretKey, "Minio secretKey为空");? ? ? ?this.minioClient = new MinioClient(this.host, this.accessKey, this.secretKey);? }
?
?
?? ?/**? ? * 上传? ? */? ?public String putObject(MultipartFile multipartFile) throws Exception {? ? ? ?// bucket 不存在,创建? ? ? ?if (!minioClient.bucketExists(this.bucket)) {? ? ? ? ? ?minioClient.makeBucket(this.bucket);? ? ? }? ? ? ?try (InputStream inputStream = multipartFile.getInputStream()) {? ? ? ? ? ?// 上传文件的名称? ? ? ? ? ?String fileName = multipartFile.getOriginalFilename();? ? ? ? ? ?// PutObjectOptions,上传配置(文件大小,内存中文件分片大小)? ? ? ? ? ?PutObjectOptions putObjectOptions = new PutObjectOptions(multipartFile.getSize(), PutObjectOptions.MIN_MULTIPART_SIZE);? ? ? ? ? ?// 文件的ContentType? ? ? ? ? ?putObjectOptions.setContentType(multipartFile.getContentType());? ? ? ? ? ?minioClient.putObject(this.bucket, fileName, inputStream, putObjectOptions);? ? ? ? ? ?// 返回访问路径? ? ? ? ? ?return this.url + UriUtils.encode(fileName, StandardCharsets.UTF_8);? ? ? }? }
?? ?/**? ? * 文件下载? ? */? ?public void download(String fileName, HttpServletResponse response){? ? ? ?// 从链接中得到文件名? ? ? ?InputStream inputStream;? ? ? ?try {? ? ? ? ? ?MinioClient minioClient = new MinioClient(host, accessKey, secretKey);? ? ? ? ? ?ObjectStat stat = minioClient.statObject(bucket, fileName);? ? ? ? ? ?inputStream = minioClient.getObject(bucket, fileName);? ? ? ? ? ?response.setContentType(stat.contentType());? ? ? ? ? ?response.setCharacterEncoding("UTF-8");? ? ? ? ? ?response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));? ? ? ? ? ?byte[] buffer = new byte[1024];? ? ? ? ? ?int length;? ? ? ? ? ?while ((length = inputStream.read(buffer)) > 0) {? ? ? ? ? ? ? ?response.getOutputStream().write(buffer, 0, length);? ? ? ? ? }? ? ? ? ? ?inputStream.close();? ? ? } catch (Exception e){? ? ? ? ? ?e.printStackTrace();? ? ? ? ? ?System.out.println("有异常:" + e);? ? ? }? }
?? ?/**? ? * 列出所有存储桶名称? ? *? ? * @return? ? * @throws Exception? ? */? ?public List<String> listBucketNames()? ? ? ? ? ?throws Exception {? ? ? ?List<Bucket> bucketList = listBuckets();? ? ? ?List<String> bucketListName = new ArrayList<>();? ? ? ?for (Bucket bucket : bucketList) {? ? ? ? ? ?bucketListName.add(bucket.name());? ? ? }? ? ? ?return bucketListName;? }
?? ?/**? ? * 查看所有桶? ? *? ? * @return? ? * @throws Exception? ? */? ?public List<Bucket> listBuckets()? ? ? ? ? ?throws Exception {? ? ? ?return minioClient.listBuckets();? }
?? ?/**? ? * 检查存储桶是否存在? ? *? ? * @param bucketName? ? * @return? ? * @throws Exception? ? */? ?public boolean bucketExists(String bucketName) throws Exception {? ? ? ?boolean flag = minioClient.bucketExists(bucketName);? ? ? ?if (flag) {? ? ? ? ? ?return true;? ? ? }? ? ? ?return false;? }
?? ?/**? ? * 创建存储桶? ? *? ? * @param bucketName? ? * @return? ? * @throws Exception? ? */? ?public boolean makeBucket(String bucketName)? ? ? ? ? ?throws Exception {? ? ? ?boolean flag = bucketExists(bucketName);? ? ? ?if (!flag) {? ? ? ? ? ?minioClient.makeBucket(bucketName);? ? ? ? ? ?return true;? ? ? } else {? ? ? ? ? ?return false;? ? ? }? }
?? ?/**? ? * 删除桶? ? *? ? * @param bucketName? ? * @return? ? * @throws Exception? ? */? ?public boolean removeBucket(String bucketName)? ? ? ? ? ?throws Exception {? ? ? ?boolean flag = bucketExists(bucketName);? ? ? ?if (flag) {? ? ? ? ? ?Iterable<Result<Item>> myObjects = listObjects(bucketName);? ? ? ? ? ?for (Result<Item> result : myObjects) {? ? ? ? ? ? ? ?Item item = result.get();? ? ? ? ? ? ? ?// 有对象文件,则删除失败? ? ? ? ? ? ? ?if (item.size() > 0) {? ? ? ? ? ? ? ? ? ?return false;? ? ? ? ? ? ? }? ? ? ? ? }? ? ? ? ? ?// 删除存储桶,注意,只有存储桶为空时才能删除成功。? ? ? ? ? ?minioClient.removeBucket(bucketName);? ? ? ? ? ?flag = bucketExists(bucketName);? ? ? ? ? ?if (!flag) {? ? ? ? ? ? ? ?return true;? ? ? ? ? }
?? ? ? }? ? ? ?return false;? }
?? ?/**? ? * 列出存储桶中的所有对象? ? *? ? * @param bucketName 存储桶名称? ? * @return? ? * @throws Exception? ? */? ?public Iterable<Result<Item>> listObjects(String bucketName) throws Exception {? ? ? ?boolean flag = bucketExists(bucketName);? ? ? ?if (flag) {? ? ? ? ? ?return minioClient.listObjects(bucketName);? ? ? }? ? ? ?return null;? }
?? ?/**? ? * 列出存储桶中的所有对象名称? ? *? ? * @param bucketName 存储桶名称? ? * @return? ? * @throws Exception? ? */? ?public List<String> listObjectNames(String bucketName) throws Exception {? ? ? ?List<String> listObjectNames = new ArrayList<>();? ? ? ?boolean flag = bucketExists(bucketName);? ? ? ?if (flag) {? ? ? ? ? ?Iterable<Result<Item>> myObjects = listObjects(bucketName);? ? ? ? ? ?for (Result<Item> result : myObjects) {? ? ? ? ? ? ? ?Item item = result.get();? ? ? ? ? ? ? ?listObjectNames.add(item.objectName());? ? ? ? ? }? ? ? }? ? ? ?return listObjectNames;? }
?? ?/**? ? * 删除一个对象? ? *? ? * @param bucketName 存储桶名称? ? * @param objectName 存储桶里的对象名称? ? * @throws Exception? ? */? ?public boolean removeObject(String bucketName, String objectName) throws Exception {? ? ? ?boolean flag = bucketExists(bucketName);? ? ? ?if (flag) {? ? ? ? ? ?List<String> objectList = listObjectNames(bucketName);? ? ? ? ? ?for (String s : objectList) {? ? ? ? ? ? ? ?if(s.equals(objectName)){? ? ? ? ? ? ? ? ? ?minioClient.removeObject(bucketName, objectName);? ? ? ? ? ? ? ? ? ?return true;? ? ? ? ? ? ? }? ? ? ? ? }? ? ? }? ? ? ?return false;? }
?? ?/**? ? * 文件访问路径? ? *? ? * @param bucketName 存储桶名称? ? * @param objectName 存储桶里的对象名称? ? * @return? ? * @throws Exception? ? */? ?public String getObjectUrl(String bucketName, String objectName) throws Exception {? ? ? ?boolean flag = bucketExists(bucketName);? ? ? ?String url = "";? ? ? ?if (flag) {? ? ? ? ? ?url = minioClient.getObjectUrl(bucketName, objectName);? ? ? }? ? ? ?return url;? }
?
}
4. 编写测试类进行测试
package com.suwell.serv.ocr.controller;
?
import com.suwell.serv.ocr.component.MinioConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
?
import javax.servlet.http.HttpServletResponse;
import java.util.List;
?
@RestController
@CrossOrigin
@RequestMapping("/test")
public class MinioController {
?? ?@Autowired? ?MinioConfig minioConfig;
?? ?// 上传? ?@PostMapping("/upload")? ?public Object upload(@RequestParam("file") MultipartFile multipartFile) throws Exception {? ? ? ?return this.minioConfig.putObject(multipartFile);? }
?? ?// 下载文件? ?@GetMapping("/download")? ?public void download(@RequestParam("fileName")String fileName, HttpServletResponse response) {? ? ? ?this.minioConfig.download(fileName,response);? }
?? ?// 列出所有存储桶名称? ?@PostMapping("/list")? ?public List<String> list() throws Exception {? ? ? ?return this.minioConfig.listBucketNames();? }
?? ?// 创建存储桶? ?@PostMapping("/createBucket")? ?public boolean createBucket(String bucketName) throws Exception {? ? ? ?return this.minioConfig.makeBucket(bucketName);? }
?? ?// 删除存储桶? ?@PostMapping("/deleteBucket")? ?public boolean deleteBucket(String bucketName) throws Exception {? ? ? ?return this.minioConfig.removeBucket(bucketName);? }
?? ?// 列出存储桶中的所有对象名称? ?@PostMapping("/listObjectNames")? ?public List<String> listObjectNames(String bucketName) throws Exception {? ? ? ?return this.minioConfig.listObjectNames(bucketName);? }
?? ?// 删除一个对象? ?@PostMapping("/removeObject")? ?public boolean removeObject(String bucketName, String objectName) throws Exception {? ? ? ?return this.minioConfig.removeObject(bucketName, objectName);? }
?? ?// 文件访问路径? ?@PostMapping("/getObjectUrl")? ?public String getObjectUrl(String bucketName, String objectName) throws Exception {? ? ? ?return this.minioConfig.getObjectUrl(bucketName, objectName);? }
}
5. 使用postman进行本地测试

6. 最后查看public桶中是否有刚才上传的文件就可以了。如果有则表明你的项目已经成功集成minio了。
以上就是springboot项目如何集成minio的全部内容了,如果对你有帮助我会感到非常荣幸。
相关文章:
SpringBoot项目集成MinIO
最近在学习MinIO,所以想让自己的SpringBoot项目集成MinIO,在网上查阅资料,并进行操作的过程中遇到一些问题,所以想把自己遇到的坑和完成步骤记录下来供自己和各位查阅。 一. MinIO的下载安装以及基本使用 1. 下载地址:https://d…...
第30篇 基于ARM A9处理器用C语言实现中断<六>
Q:怎样设计基于ARM A9处理器的C语言程序在数码管上滚动显示字符? A:使用A9 Private Timer中断源,控制字符滚动的速度;按键产生中断可以控制字符暂停/继续滚动。 本实验在DE1-SoC开发板的6个七段数码管*HEX5~HEX0*上…...
Flutter 中的单例模式
传统: class RouterManager {// 单例模式static final RouterManager _instance RouterManager._internal();factory RouterManager() {return _instance;}RouterManager._internal(); }传递参数进行初始化时: class RouterManager {// 私有静态实例&a…...
8.python文件
文章目录 1.**文件**1.1**文件是什么**1.2**文件路径**1.3**文件操作**1.3.1**打开文件**1.3.2**关闭文件**1.3.3**写文件**1.3.4**读文件** 1.4**关于中文的处理**1.5**使用上下文管理器** 大家好,我是晓星航。今天为大家带来的是 python文件 相关的讲解࿰…...
2025vue4.x全栈学习关键技术分析线路图
关键升级点说明: 编译优化 :Vue 4.x采用WASM编译提速300% 智能工具链 :Vite插件市场新增AI代码审查模块 跨平台能力 :Uni-App支持原生ARCore/ARKit调用 安全增强 :默认启用WebAuthn生物认证…...
革新之力:数字科技——重塑未来的超越想象之旅
在21世纪的科技浪潮中,数字科技如同一股不可阻挡的洪流,正以前所未有的速度和广度改变着我们的生活、工作乃至整个社会的结构。它不仅是技术的简单迭代,更是对人类社会认知边界的拓宽,对经济模式、社会治理、文化形态等多方面的深…...
超级详细,知识图谱系统的理论详解+部署过程
知识图谱系统(Knowledge Graph System)是一种用于表示、存储、查询和推理知识的系统。它通过结构化的方式将现实世界中的实体、概念及其相互关系组织成一个图结构,从而帮助机器理解和处理复杂的知识。 知识图谱的核心组成部分 实体(Entities): 实体是知识图谱中的节点,…...
电路笔记 (信号): opa tips 放大器反馈电阻并联电容抑制高频噪声的详细推导(传递函数分析)
1. 高频噪声传递函数分析 (1)电路结构 输入信号通过 R 1 R_1 R1 和 C NI C_{\text{NI}} CNI 的并联组合连接到运放的同相输入端。反馈电阻 R 2 R_2 R2 连接在运放的输出端和反相输入端之间。 Layer 1 - 30p R2 R1 R3R1//R2 IN OUT 反向放大电…...
DeepSeek安装部署笔记(一)
Ollamaopen-WebUI部署 DeepSeek安装部署笔记第一步 Ollama安装1.安装ollama:官网https://ollama.com/下载2.上面安装完成,在cmd命令行: 第二步 给DeepSeek添加OpenWebUI界面(重点)1.安装conda:用它来管理py…...
【JavaEE进阶】Spring MVC(4)-图书管理系统案例
欢迎关注个人主页:逸狼 创造不易,可以点点赞吗 如有错误,欢迎指出~ 图书管理系统 创建书籍类BookInfo import lombok.Data;import java.math.BigDecimal;Data //这个类基本上是和数据库对应起来的 public class BookInfo {private Integer id…...
Ubuntu部署ktransformers
准备工作 一台服务器 CPU:500G GPU:48G(NVIDIA4090) 系统:Ubuntu20.04(github的文档好像用的是22.04) 第一步:下载权重文件 1.下载hfd wget https://hf-mirror.com/hfd/hfd.s…...
助力DeepSeek私有化部署服务:让企业AI落地更简单、更安全
在数字化转型的浪潮中,越来越多的企业选择私有化部署AI技术,以保障数据安全、提升业务效率并实现自主可控。DeepSeek作为行业领先的AI开源技术,其技术可以支持企业私有化部署,企业需要一站式服务私有化部署,涵盖硬件采…...
面试官询问项目前后端人员配比之高分示范回答
面试官询问项目前后端人员配比之高分示范回答 以下是对两个项目前后端人员配置的精准分析,结合 技术复杂度、协作效率、风险控制 三个维度设计回答,突出合理性与团队协作意识: 一、《x能x服》项目(Vue重构) 1. 人员配置与分工 前端:1人(独立开发) 负责旧系统业务逻辑…...
MyBatis中的日志和映射器说明
1.MyBatis中的日志 1.1 什么是日志 在我们编写应用的时候,有一些信息需要及时查看,查看的时候有时需要输出到控制台,有时需要输出到文件。MyBatis也需要日志,一般情况下,使用log4j进行日志管理。 1.2 在MyBatis中…...
深入了解 Pinia:Vue 的下一代状态管理工具 (上篇)
引言 在现代前端开发中,状态管理是构建复杂应用的关键。Vue 生态系统中,Vuex 一直是官方推荐的状态管理工具。然而,随着 Vue 3 的发布,一个新的状态管理工具——Pinia,逐渐崭露头角。Pinia 不仅继承了 Vuex 的优点&am…...
Unity 中导入的VRM模型渲染为VRoid风格
按照前篇Unity VRoidBlenderUnity 3D人物模型导入使用-CSDN博客 导入到Unity之后,可以按需调整模型在场景中的渲染表现, 但是按照教程中完成的情况, 整个模型没有进行材质区分, 仅用一个材质表现整个模型, 导致不能给不同部位进行渲染调整. 这里我希望能够在Unity中获得跟VRoi…...
【ELK】【Elasticsearch 】DSL 和 DQL
1. DSL 查询(Query DSL) 全称:Domain Specific Language(领域特定语言)。 定义:Elasticsearch 提供的一种基于 JSON 的查询语言,用于构建复杂的查询逻辑。 特点: 支持多种查询类型…...
最新版本Exoplayer扩展FFmpeg音频软解码保姆级教程
ExoPlayer 是一个开源的 Android 媒体播放库,由 Google 开发和维护,用于替代 Android 系统自带的 MediaPlayer。它提供了更强大的功能、更好的性能和更高的灵活性,适用于各种复杂的媒体播放场景。所以被广泛用于各种播放器场景。 最近项目中…...
面对低消费欲人群,我们如何开发其需求?
在市场增量放缓的当下,开发深度开发各层次的人群已经成为现实需求。低消费欲人群并非“没有需求”,而是更谨慎、更理性。他们可能对价格敏感,但对实用性、情感共鸣和生活品质的追求依然存在。就让我们从以下四个角度,拆解如何激发…...
《算法基础入门:最常用的算法详解与应用(持续更新实战与面试题)》
1. 排序算法 排序算法是将一组数据按特定的顺序排列起来的算法,常见的有: 冒泡排序(Bubble Sort)选择排序(Selection Sort)插入排序(Insertion Sort)归并排序(Merge So…...
国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
Linux中《基础IO》详细介绍
目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改,实现简单cat命令 输出信息到显示器,你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...
Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践
在 Kubernetes 集群中,如何在保障应用高可用的同时有效地管理资源,一直是运维人员和开发者关注的重点。随着微服务架构的普及,集群内各个服务的负载波动日趋明显,传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...
热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁
赛门铁克威胁猎手团队最新报告披露,数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据,严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能,但SEMR…...
