在 Spring Boot 中集成 MinIO 对象存储
MinIO 是一个开源的对象存储服务器,专注于高性能、分布式和兼容S3 API的存储解决方案。本文将介绍如何在 Spring Boot 应用程序中集成 MinIO,以便您可以轻松地将对象存储集成到您的应用中。
安装minio
-
拉取 minio Docker镜像
docker pull minio/minio -
创建minio数据和配置目录
mkdir -p /data/minio/data /data/minio/config -
运行minio容器,设置访问和密钥并挂载数据卷
docker run -p 9090:9090 -p 9000:9000 --name minio \-v ~/data/minio/data:/data \-v ~/data/minio/config:/root/.minio \-e "MINIO_ACCESS_KEY=minio" \-e "MINIO_SECRET_KEY=minio123" \minio/minio server /data --console-address ":9090"
或者 使用 docker-compose.yml
version: '3.9'
services:minio:command: 'server /data --console-address ":9090"'image: minio/minioenvironment:- MINIO_SECRET_KEY=minio123- MINIO_ACCESS_KEY=miniovolumes:- '~/data/minio/config:/root/.minio'- '~/data/minio/data:/data'container_name: minioports:- '9000:9000'- '9090:9090'
mkdir``~/minio/data在您的主目录中创建一个新的本地目录。docker run启动 MinIO 容器。-p将本地端口绑定到容器端口。-name为容器创建一个名称。-v将文件路径设置为容器要使用的持久卷位置。当 MinIO 将数据写入 时/data,该数据会镜像到本地路径~/minio/data,从而允许其在容器重新启动之间保留。您可以替换~/minio/data为用户具有读取、写入和删除访问权限的另一个本地文件位置。-e分别设置环境变量MINIO_ROOT_USER和MINIO_ROOT_PASSWORD。这些设置root 用户凭据。更改用于您的容器的示例值。
运行结果
WARNING: MINIO_ACCESS_KEY and MINIO_SECRET_KEY are deprecated.Please use MINIO_ROOT_USER and MINIO_ROOT_PASSWORD
Formatting 1st pool, 1 set(s), 1 drives per set.
WARNING: Host local has more than 0 drives of set. A host failure will result in data becoming unavailable.
MinIO Object Storage Server
Copyright: 2015-2023 MinIO, Inc.
License: GNU AGPLv3 <https://www.gnu.org/licenses/agpl-3.0.html>
Version: RELEASE.2023-08-29T23-07-35Z (go1.19.12 linux/amd64)Status: 1 Online, 0 Offline.
S3-API: http://172.17.0.2:9000 http://127.0.0.1:9000
Console: http://172.17.0.2:9090 http://127.0.0.1:9090 Documentation: https://min.io/docs/minio/linux/index.html
Warning: The standard parity is set to 0. This can lead to data loss.
- 登录 控制台 http://127.0.0.1:9090/browser 用户名密码是你启动时初始化的这里是 minio、minio123
- 创建 桶(Buckets)

参考:
- https://min.io/docs/minio/container/index.html
Spring Boot 集成
1. 添加 MinIO 客户端依赖
在您的 Spring Boot 项目的 pom.xml 文件中添加 MinIO 客户端依赖:
<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.5.5</version>
</dependency>
接下来,您需要在您的Spring Boot应用程序中配置MinIO客户端。您可以在application.properties或application.yml文件中添加以下配置:
# MinIO配置
minio.endpoint=http://localhost:9000 # MinIO服务器的地址
minio.access-key=minio # 您的访问密钥
minio.secret-key=minio123 # 您的秘密密钥
2. 创建 MinIO 配置类
创建一个配置类,用于初始化 MinIO 客户端。创建一个名为 MinioConfig 的类:
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;@Beanpublic MinioClient minioClient() {return MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();}
}
3. 集成 MinIO 操作到您的应用
您可以在您的服务或控制器中使用 MinioClient 来进行 MinIO 操作。以下是一个上传文件到 MinIO 的示例:
import io.minio.GetObjectArgs;
import io.minio.GetObjectResponse;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import okhttp3.Headers;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;@RestController
@RequestMapping("/minio")
public class MinioController {@Autowiredprivate MinioClient minioClient;@PostMapping("/upload")public String uploadFile(@RequestParam("file") MultipartFile file) {try {/*** 使用 UUID 作为对象名称:* 1. 唯一性,避免对象名称冲突。* 2. 隐藏实际文件信息,提高一定的隐私。* 3. 对象名称不受原始文件名长度或特殊字符的限制。* 在实际场景中,如果你更注重文件的可识别性和管理,可以考虑使用文件名称。如果你更注重唯一性和隐私性,可以考虑使用 UUID。* 同时,你也可以结合两者,例如将文件名作为对象属性存储,然后使用 UUID 作为对象名称。这样既可以保留文件信息,又能保证唯一性。*/String objectName = UUID.randomUUID().toString();Map<String, String> metadata = new HashMap<>();metadata.put("originalFilename", file.getOriginalFilename());minioClient.putObject(PutObjectArgs.builder().bucket("firsttest").object(objectName).userMetadata(metadata).stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build());return "File uploaded successfully! fileId: " + objectName;} catch (Exception e) {return "Error uploading file: " + e.getMessage();}}@GetMapping("/download/{filename}")public ResponseEntity<InputStreamResource> downloadFile(@PathVariable("filename") String filename) {try {Optional<GetObjectResponse> inputStream = Optional.ofNullable(minioClient.getObject(GetObjectArgs.builder().bucket("firsttest").object(filename).build()));if (inputStream.isPresent()) {InputStreamResource resource = new InputStreamResource(inputStream.get());Headers headers = inputStream.get().headers();ResponseEntity.BodyBuilder bodyBuilder = ResponseEntity.ok();String mediaType = headers.get("Content-Type");if (StringUtils.isNotEmpty(mediaType)) {bodyBuilder.contentType(MediaType.parseMediaType(mediaType));}return bodyBuilder.header("Content-Disposition", "attachment; filename=" + URLEncoder.encode(headers.get("X-Amz-Meta-originalfilename"), StandardCharsets.UTF_8)).body(resource);} else {return ResponseEntity.notFound().build();}} catch (Exception e) {return ResponseEntity.badRequest().body(null);}}}
使用 UUID 作为对象名称:
- 唯一性,避免对象名称冲突。
- 隐藏实际文件信息,提高一定的隐私。
- 对象名称不受原始文件名长度或特殊字符的限制。
在实际场景中,如果你更注重文件的可识别性和管理,可以考虑使用文件名称。如果你更注重唯一性和隐私性,可以考虑使用 UUID。
同时,你也可以结合两者,例如将文件名作为对象属性存储,然后使用 UUID 作为对象名称。这样既可以保留文件信息,又能保证唯一性。
http://127.0.0.1:80/minio/upload 上传完毕成功后,控制台查看文件




自定义 metadata 为啥前缀有 X-Amz-Meta-?
在 MinIO 中,当你设置自定义元数据(metadata)时,MinIO 会遵循 Amazon S3 的元数据规范,其中元数据的键名> 前会添加
x-amz-meta-前缀。这是因为 MinIO 是一个兼容 Amazon S3 API 的对象存储服务器,因此它采用了 Amazon > S3 的一些规范和约定。
Amazon S3 使用x-amz-meta-前缀来标识用户自定义的元数据,以便与 Amazon S3 内部使用的标准元数据进行区> 分。这样可以确保用户自定义的元数据不会与 S3 内部使用的元数据冲突。这也是为了保持兼容性,使 MinIO 用户可以> 使用与 Amazon S3 相同的元数据命名约定。
因此,在 MinIO 中设置自定义元数据时,不需要手动添加x-amz-meta-前缀,MinIO 会自动处理这个前缀,确保它符合 Amazon S3 的规范。当你从对象中检索元数据时,MinIO 也会自动解析并返回合适的键名,不包含前缀。
总之,这个前缀是 MinIO 为了兼容 Amazon S3 API,保持统一性而引入的。
请注意,上述示例仅为基本示例,用于展示如何在Spring Boot中集成MinIO。您可以根据您的实际需求进行更多的配置和操作。确保替换示例中的"your-access-key"、“your-secret-key”、"your-bucket-name"和其他参数为您自己的值。
最后,不要忘记在您的MinIO服务器上创建对应的存储桶(Bucket),以及设置正确的访问权限。
相关文章:
在 Spring Boot 中集成 MinIO 对象存储
MinIO 是一个开源的对象存储服务器,专注于高性能、分布式和兼容S3 API的存储解决方案。本文将介绍如何在 Spring Boot 应用程序中集成 MinIO,以便您可以轻松地将对象存储集成到您的应用中。 安装minio 拉取 minio Docker镜像 docker pull minio/minio创…...
seq2seq与引入注意力机制的seq2seq
1、什么是 seq2seq? 就是字面意思,“句子 到 句子”。比如翻译。 2、seq2seq 有一些特点 seq2seq 的整体架构是 “编码器-解码器”。 其中,编码器是 RNN,并将 最后一个hidden state(隐藏状态)【即&…...
【zookeeper】zookeeper介绍
分布式协调技术 在学习ZooKeeper之前需要先了解一种技术——分布式协调技术。那么什么是分布式协调技术?其实分布式协调技术主要用来解决分布式环境当中多个进程之间的同步控制,让他们有序的去访问某种临界资源,防止造成"脏数据"的…...
2023高教社杯数学建模思路 - 案例:ID3-决策树分类算法
文章目录 0 赛题思路1 算法介绍2 FP树表示法3 构建FP树4 实现代码 建模资料 0 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 1 算法介绍 FP-Tree算法全称是FrequentPattern Tree算法,就是频繁模…...
springboot docker
在Spring Boot中使用Docker可以帮助你将应用程序与其依赖的容器化,并简化部署和管理过程。 当你在Spring Boot中使用Docker时,你的代码不需要特殊的更改。你可以按照通常的方式编写Spring Boot应用程序。 java示例代码,展示了如何编写一个基…...
docker-compose 部署nacos 整合 postgresql 为DB
标题docker-compose 部署nacos 整合 postgresql 为DB 前提: 已经安装好postgresql数据库 先创建好一个数据库 nacos,执行以下sql: /** Copyright 1999-2018 Alibaba Group Holding Ltd.** Licensed under the Apache License, Version 2.0 (the "…...
详解 ElasticSearch Kibana 配置部署
默认安装部署所在机器允许外网 SSH工具 Putty 链接:https://pan.baidu.com/s/1b6gumtsjL_L64rEsOdhd4A 提取码:lxs9 Winscp 链接:https://pan.baidu.com/s/1tD8_2knvv0EJ5OYvXP6VTg 提取码:lxs9 WinSCP安装直接下一步到完成…...
SourceTree 使用技巧
参考资料 SourceTree使用教程(一)—克隆、提交、推送SourceTree的软合并、混合合并、强合并区别SourceTree 合并分支上的多个提交,一次性合并分支的多次提交至另一分支,主分支前进时的合并冲突解决 目录 一. 基础设置1.1 用户信息…...
VIRTIO-BLK代码分析(0)概述
也无风雨也无晴。- 苏轼(宋) 接下来介绍VIRTIO相关内容。首先从VIRTIO-BLK开始分析,VIRTIO-BLK各部分交互图如下所示: 这里包含以下几个部分: Guest UserSpace:虚拟机用户空间,如虚拟机中运行f…...
【2023年11月第四版教材】第10章《进度管理》(第一部分)
第10章《进度管理》(第一部分) 1 章节说明2 管理基础3 管理过程3.1 管理的过程★★★3.2 管理ITTO汇总★★★ 1 章节说明 【本章分值预测】大部分内容不变,细节有一些变化,预计选择题考3-4分,案例和论文 都有可能考&a…...
【多线程案例】生产者消费者模型(堵塞队列)
文章目录 1. 什么是堵塞队列?2. 堵塞队列的方法3. 生产者消费者模型4. 自己实现堵塞队列 1. 什么是堵塞队列? 堵塞队列也是队列,故遵循先进先出的原则。但堵塞队列是一种线程安全的数据结构,可以避免线程安全问题,当队…...
数据结构与算法基础-学习-30-插入排序之直接插入排序、二分插入排序、希尔排序
一、排序概念 将一组杂乱无章的数据按一定规律顺次排列起来。 将无序序列排成一个有序序列(由小到大或由大到小)的运算。 二、排序方法分类 1、按数据存储介质 名称描述内部排序数据量不大、数据在内存,无需内外交换存交换存储。外部排序…...
Qt+C++桌面计算器源码
程序示例精选 QtC桌面计算器源码 如需安装运行环境或远程调试,见文章底部个人QQ名片,由专业技术人员远程协助! 前言 这篇博客针对<<QtC桌面计算器源码>>编写代码,代码整洁,规则,易读。 学习与…...
kubesphere安装Maven+JDK17 流水线打包
kubesphere 3.4.0版本,默认支持的jav版本是8和11,不支持17 。需要我们自己定义JenKins Agent 。方法如下: 一、构建镜像 1、我们需要从Jenkins Agent的github仓库拉取master最新源码,最新源码里已经支持jdk17了。 git clone ht…...
百度搜索清理大量低质量网站
我是卢松松,点点上面的头像,欢迎关注我哦! 据部分站长爆料:百度大规模删低质量网站的百度资源站长平台权限,很多网站都被删除了百度站长资源平台后台权限,以前在百度后台添加的网站大量被删除!…...
WPF数据模板
样式提供了基本的格式化能力,但它们不能消除到目前为止看到的列表的最重要的局限性:不管如何修改ListBoxItem,它都只是ListBoxItem,而不是功能更强大的元素组合。并且因为每个ListBoxItem只支持单个绑定字段,所以不可能…...
浙江绿农环境:将废弃矿山变耕地,为生态文明贡献力量
近年来,随着可持续发展理念在中国乃至全球的日益普及,浙江绿农生态环境有限公司以其独特的创新和实践,成为了绿色发展的典范,在奋进新时代、建设新天堂的背景下,绿农环境在杭州市固废治理行业迈出坚实的步伐࿰…...
HTML/CSS盒子模型
盒子:页面中的所有的元素(标签),都可以看做一个盒子,由盒子将页面中的元素包含在一个矩形区域内,通过盒子的视角更加方便的进行页面布局 盒子模型的组成: 内容区域(contentÿ…...
《Java面向对象程序设计》学习笔记——CSV文件的读写与处理
笔记汇总:《Java面向对象程序设计》学习笔记 笔记记录的不是非常详实,如果有补充的建议或纠错,请踊跃评论留言!!! 什么是CSV文件 CSV文件的定义 CSV 是英文 comma-separated values 的缩写࿰…...
opencv 案例05-基于二值图像分析(简单缺陷检测)
缺陷检测,分为两个部分,一个部分是提取指定的轮廓,第二个部分通过对比实现划痕检测与缺角检测。本次主要搞定第一部分,学会观察图像与提取图像ROI对象轮廓外接矩形与轮廓。 下面是基于二值图像分析的大致流程 读取图像将图像转换…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...
