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…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
Qemu arm操作系统开发环境
使用qemu虚拟arm硬件比较合适。 步骤如下: 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载,下载地址:https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...
系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文通过代码驱动的方式,系统讲解PyTorch核心概念和实战技巧,涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...
深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向
在人工智能技术呈指数级发展的当下,大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性,吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型,成为释放其巨大潜力的关键所在&…...
水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关
在水泥厂的生产流程中,工业自动化网关起着至关重要的作用,尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关,为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多,其中不少设备采用Devicenet协议。Devicen…...
