springboot 对接 minio 分布式文件系统
1. minio介绍
Minio 是一个基于Go语言的对象存储服务。它实现了大部分亚马逊S3云存储服务接口,可以看做是是S3的开源版本,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。区别于分布式存储系统,minio的特色在于简单、轻量级,对开发者友好,认为存储应该是一个开发问题而不是一个运维问题。
2. minio下载地址
下载
3. liunx minio文件授权
chomd +x minio
![]()
4. 编写启动minio shell文件
vi run.sh
#!/bin/bash
#web管理界面登录用户
export MINIO_ROOT_USER=minio
#web管理界面登录密码
export MINIO_ROOT_PASSWORD=minio
#生成共享链接时,需要配置,否则是本地地址127.0.0.1,有地址的可以修改为自己地址
export MINIO_SERVER_URL=http://IP:9002
# nohup启动服务 指定文件存放路径 /root/data 还有设置日志文件路径 /root/minio/log
nohup ./minio server --address :9002 --console-address :9001 /root/data/minio > /root/logs/minio.log 2>&1 &
5. 赋权限给予shell文件run.sh文件
chmod u+x run.sh
.
6. 启动minio服务,执行sh文件
bash run.sh
![]()
7. 查看日志,我们在4的时候最后一条上有配置log地址
tail -f /root/logs/minio.log
![]()

8. 浏览器访问minio界面,并且输入在第四步配置的账号密码

9. 接下来我们可以创建一个我们作为测试的文件桶


10. 当我们创建好桶之后,我们可以前往查看是否存在

11. 点击桶进入,手动测试上传文件

12. 上传文件之后我们可以选择某一个文件进行下载或者链接共享,链接共享默认时间为7天

13. 当我们点击共享时,会出现一个共享链接,我们可以直接在浏览器内查看相对应的文件


14. springboot 对接minio,加入POM文件
<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>7.1.0</version></dependency>
15. 配置application文件
生成请求账号密码


minio:endpoint: http://IP:9002accessKey: bjdZxvMDxAzYETgYn0aY 配置账号secretKey: uk7srkLHsYkwzvTYVzDBtwzlXz5fxsoMmNpbb3SN 配置密码bucketName: test 桶名称-默认
16.springboot 工具类
package com.project.google.util;/*** @Description: TODO* @Author xgp* @Date 2023/8/7 8:05* @PackageName:com.project.google.util* @ClassName: MinioTemplate* @Version 1.0*/import io.minio.*;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.SneakyThrows;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;/*** Minio 基础操作类** @author: zhanghuaiyu* @since 2021-01-22 16:27*/
@Configuration
public class MinioTemplate implements InitializingBean {private MinioClient minioClient;@Value("${minio.endpoint}")private String url;@Value("${minio.accessKey}")private String accessKey;@Value("${minio.secretKey}")private String secretKey;@Overridepublic void afterPropertiesSet() {Assert.hasText(url, "Minio url 为空");Assert.hasText(accessKey, "Minio accessKey为空");Assert.hasText(secretKey, "Minio secretKey为空");this.minioClient = new MinioClient(url, accessKey, secretKey);}/*** 创建bucket* setBucketPolicy 设置权限才可以预览** @param bucketName bucket名称*/@SneakyThrowspublic Boolean createBucket(String bucketName) {if (!bucketExists(bucketName)) {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());StringBuilder builder = new StringBuilder();builder.append("{\n");builder.append(" \"Statement\": [\n");builder.append(" {\n");builder.append(" \"Action\": [\n");builder.append(" \"s3:GetBucketLocation\",\n");builder.append(" \"s3:ListBucket\"\n");builder.append(" ],\n");builder.append(" \"Effect\": \"Allow\",\n");builder.append(" \"Principal\": \"*\",\n");builder.append(" \"Resource\": \"arn:aws:s3:::bucketname\"\n");builder.append(" },\n");builder.append(" {\n");builder.append(" \"Action\": \"s3:GetObject\",\n");builder.append(" \"Effect\": \"Allow\",\n");builder.append(" \"Principal\": \"*\",\n");builder.append(" \"Resource\": \"arn:aws:s3:::my-bucketname/*.*\"\n");builder.append(" }\n");builder.append(" ],\n");builder.append(" \"Version\": \"2012-10-17\"\n");builder.append("}\n");minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(builder.toString().replace("bucketname", bucketName)).build());return true;} else {return false;}}/*** 获取全部bucket* <p>* https://docs.minio.io/cn/java-client-api-reference.html#listBuckets*/@SneakyThrowspublic List<Bucket> getAllBuckets() {return minioClient.listBuckets();}/*** 根据bucketName获取信息** @param bucketName bucket名称*/@SneakyThrowspublic Optional<Bucket> getBucket(String bucketName) {return minioClient.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();}/*** 根据bucketName删除信息** @param bucketName bucket名称*/@SneakyThrowspublic void removeBucket(String bucketName) {minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());}/*** 根据文件前置查询文件** @param bucketName bucket名称* @param prefix 前缀* @param recursive 是否递归查询* @return MinioItem 列表*/@SneakyThrowspublic List getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) {List<Item> list = new ArrayList<>();Iterable<Result<Item>> objectsIterator = minioClient.listObjects(bucketName, prefix, recursive);if (objectsIterator != null) {Iterator<Result<Item>> iterator = objectsIterator.iterator();if (iterator != null) {while (iterator.hasNext()) {Result<Item> result = iterator.next();Item item = result.get();list.add(item);}}}return list;}/*** 获取文件外链** @param bucketName bucket名称* @param objectName 文件名称* @param expires 过期时间 <=7* @return url*/@SneakyThrowspublic String getObjectUrl(String bucketName, String objectName, Integer expires) {return minioClient.presignedGetObject(bucketName, objectName, expires);}/*** 获取文件路径** @param bucketName* @param fileName* @return*/@SneakyThrowspublic String getObjectUrl(String bucketName, String fileName) {return minioClient.getObjectUrl(bucketName, fileName);}/*** 获取文件** @param bucketName bucket名称* @param objectName 文件名称* @return 二进制流*/@SneakyThrowspublic InputStream getObject(String bucketName, String objectName) {return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());}/*** 获取文件** @param bucketName* @param objectName* @return*/@SneakyThrowspublic ObjectStat statObject(String bucketName, String objectName) {return minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());}/*** 上传文件** @param bucketName bucket名称* @param objectName 文件名称* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject*/public String putObject(String bucketName, String objectName, MultipartFile file) throws Exception {if (!this.bucketExists(bucketName)) {this.createBucket(bucketName);}minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(file.getInputStream(), file.getSize(), PutObjectArgs.MIN_MULTIPART_SIZE).contentType(file.getContentType()).build());return bucketName;}/*** 上传文件** @param bucketName bucket名称* @param objectName 文件名称* @param stream 文件流* @param size 大小* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject*/public void putObject(String bucketName, String objectName, InputStream stream, long size) throws Exception {minioClient.putObject(bucketName, objectName, stream, new PutObjectOptions(stream.available(), -1));}/*** 获取文件信息, 如果抛出异常则说明文件不存在** @param bucketName bucket名称* @param objectName 文件名称* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#statObject*/public ObjectStat getObjectInfo(String bucketName, String objectName) throws Exception {return minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());}/*** 删除文件** @param bucketName bucket名称* @param objectName 文件名称* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#removeObject*/public void removeObject(String bucketName, String objectName) throws Exception {minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());}/*** 批量删除文件夹内所有文件** @param bucketName bucket名称* @param objectName 文件名称* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#removeObject*/public void removeObjects(String bucketName, String objectName) throws Exception {List<Item> list = getAllObjectsByPrefix(bucketName, objectName, false);for (Item item : list) {removeObject(bucketName, item.objectName());}}@SneakyThrowspublic boolean bucketExists(String bucketName) {return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());}/*** 文件下载** @param response* @param bucket* @param objectName* @param outName* @throws Exception*/public void download(HttpServletResponse response, String bucket, String objectName, String outName) throws Exception {ObjectStat stat = this.statObject(bucket, objectName);response.setContentType(stat.contentType());response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(outName, "UTF-8"));response.setHeader("FileName", URLEncoder.encode(outName, "UTF-8"));InputStream in = this.getObject(bucket, objectName);IOUtils.copy(in, response.getOutputStream());in.close();}/*** 合并分片文件到指定目录** @param bucket* @param fileName* @param sources* @return* @throws Exception*/public ObjectWriteResponse composeObject(String bucket, String fileName, List<ComposeSource> sources) throws Exception {ObjectWriteResponse response = minioClient.composeObject(ComposeObjectArgs.builder().bucket(bucket).object(fileName).sources(sources).build());return response;}
}
17.请求测试controller方法
package com.project.google.controller;import afu.org.checkerframework.checker.oigj.qual.O;
import com.project.google.util.MinioTemplate;
import io.minio.messages.Bucket;
import org.apache.commons.io.IOUtils;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.io.InputStream;
import java.util.*;/*** @Description: TODO* @Author xgp* @Date 2023/8/7 8:35* @PackageName:com.project.google.controller* @ClassName: TbMinioController* @Version 1.0*/
@RestController
public class TbMinioController {@Autowiredprivate MinioTemplate minioTemplate;//创建新的桶@GetMapping("createBucket")public Object createBucket(String bucketName){return minioTemplate.createBucket(bucketName);}//获取对应桶信息@GetMapping("getList")public Object getList(String bucketName){Bucket bucket = minioTemplate.getBucket(bucketName).get();Map<String,Object> map = new HashMap<>();map.put("name",bucket.name());map.put("createDate",bucket.creationDate());return map;}//获取所有桶信息@GetMapping("getAll")public Object getAll(){List<Map> list = new ArrayList<>();List<Bucket> buckets = minioTemplate.getAllBuckets();buckets.stream().forEach(item -> {Map<String,Object> map = new HashMap<>();map.put("name",item.name());map.put("createDate",item.creationDate());list.add(map);});return list;}/**上传文件到对应桶里,如果你想放入指定文件夹,传入文件名前面带上文件夹名称及路径,比如 test文件夹就- test/文件名,依次类推*/@PostMapping("uploadFile")public Object uploadFile(@RequestParam("file") MultipartFile file) throws Exception {/**文件夹属性,可加载文件前方*/return minioTemplate.putObject("test","uu/" + file.getOriginalFilename(),file);}/**获取图片信息,二进制数据转换为图片呈现*/@GetMapping(value = "getFile", produces = MediaType.IMAGE_JPEG_VALUE)public byte[] getFile(@RequestParam("bucketName") String bucketName,@RequestParam("objectName") String objectName) throws IOException {InputStream stream = minioTemplate.getObject(bucketName, objectName);byte[] bytes = IOUtils.toByteArray(stream);return bytes;}/**获取图片分享链接,expires为过期时间,可为小于等于7*/@GetMapping(value = "getObjectUrl")public String getObjectUrl(@RequestParam("bucketName") String bucketName,@RequestParam("objectName") String objectName) throws IOException {return minioTemplate.getObjectUrl(bucketName, objectName, 1);}}
18.接口测试
18.1 查询test bucket信息

18.2 获取所有bucket信息

18.3 上传文件,我这边是通过apipox进行测试


18.4 查看图片信息

18.5 生成共享链接


到此,整个对接过程就已经差不多了,其他扩展功能,如有需要,可以咨询这边,给出解答或者思路。
相关文章:
springboot 对接 minio 分布式文件系统
1. minio介绍 Minio 是一个基于Go语言的对象存储服务。它实现了大部分亚马逊S3云存储服务接口,可以看做是是S3的开源版本,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象…...
前端小练习:案例4.3D图片旋转展示(旋转木马)
一.效果预览图 二.实现思路 1.实现旋转木马效果的第一步是先准备好自己需要的图片,创建html文件 2.旋转木马的实现,关键点在3D形变和关键帧动画。 3.步骤,定义一个div使其居中,,把图片放进div盒子里,因为图…...
Linux这17个操作技巧是每个运维工程师应知必会的吧?
今天跟大家分享17个linux运维中常用的操作技巧!掌握好这些技巧,或许某一天能够让老板给你涨工资! 1、查找当前目录下所有以.tar结尾的文件然后移动到指定目录: find . -name “*.tar” -exec mv {}./backup/ ; ❝ 注解࿱…...
音视频基础:分辨率、码率、帧率之间关系
基础 人类视觉系统 分辨率 像素: 是指由图像的小方格组成的,这些小方块都有一个明确的位置和被分配的色彩数值,小方格颜色和位置就决定该图像所呈现出来的样子;可以将像素视为整个图像中不可分割的单位或者是元素;像素…...
Java基础八 - HTTP相关/Cookie/Session/网络攻击
一、 反射/序列化/拷贝 1. 反射 //反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力 //在Yaml数据驱动自动化框架比较适用,能获取到当前的类名及方法名 import java.lang.reflect.*;public class ReflectionExample {public static void main(Str…...
【车道线】TwinLiteNet 复现过程全纪录
码字不易,喜欢的请点赞收藏!!!!! 论文全文翻译:【freespace】TwinLiteNet: An Efficient and Lightweight Model for Driveable Area and Lane Segmentation_莫克_Cheney的博客-CSDN博客 目录…...
七牛云获取qn(url、bucket、access-key、secret-key)
1.注册账号 2.access-key和secret-key: 点击“密钥管理” 复制AK和SK即可 域名: bucket: 这个就是对象存储空间名字 先新建一个空间(没买需要先购买),步骤如下: 填写存储空间名字࿰…...
定时任务实现 - Cron表达式知识
Cron表达式 cron表达式是一个字符串,由6到7个字段组成,用空格分隔。其中前6个字段是必须的,最后一个是可选的。每个字段的含义为:秒 分 时 日 月 周 年 字符解释: 枚举:, (cron“7,9,23****?”):任意时刻…...
【java】抽象
java抽象 抽象类抽象方法抽象类和抽象方法 抽象类 在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就…...
Qt应用开发(基础篇)——时间微调输入框 QDateTimeEdit、QDateEdit、QTimeEdit
一、前言 QAbstractSpinBox是全部微调输入框的父类,这是一种允许用户通过点击上下箭头按钮或输入数字来调整数值的图形用户界面控件,父类提供了当前值text、对齐方式align、只读readOnly等通用属性和方法。在上一篇数值微调输入框中有详细介绍。 QDateTi…...
日撸代码300行:第63天(集成学习之 AdaBoosting-1)
代码来自闵老师”日撸 Java 三百行(61-70天) 日撸 Java 三百行(61-70天,决策树与集成学习)_闵帆的博客-CSDN博客 学习过程中理解算法参考了:(十三)通俗易懂理解——Adaboost算法原…...
抽象父类获取子类的泛型 或接口泛型
jie通过getClass().getGenericSuperclass()或者子类的泛型 getClass().getGenericInterfaces();获取多个接口的泛型 GenericTypeResolver.resolveTypeArgument(GenericityService.class, GenericitySuper.class) 抽象父类 public abstract class GenericitySuper<T> …...
题目:2341.数组能形成多少数对
题目来源: leetcode题目,网址:2341. 数组能形成多少数对 - 力扣(LeetCode) 解题思路: 使用哈希表对数组中元素及其出现次数计数后对其进行统计即可。 解题代码: class Solution {public …...
NB-IOT 和蜂窝通信(2/3/4/5G)的区别和特点是什么?
NB-IOT 和蜂窝通信(2/3/4/5G)的区别和特点是什么? 参考链接:https://www.sohu.com/a/221664826_472880 NB IOT是窄带物联网技术,主要解决的是低速率数据传输,可使用GSM900或DCS1800频段,在频段使用上比较灵活,可以和GSM,UMTS或LTE共存,具备优异的MCL(最小耦合损耗…...
vue3 动态导入src/page目录下的所有子文件,并自动注册所有页面组件
main.js添加一下代码: const importAll (modules) > {Object.keys(modules).forEach((key) > {const component key.replace(/src/, /).replace(.vue, );const componentName key.split(/).slice(-2, -1)[0] -page;app.component(componentName, modules…...
python优雅地爬虫
申明:仅用作学习用途,不提供任何的商业价值。 背景 我需要获得新闻,然后tts,在每天上班的路上可以听一下。具体的方案后期我也会做一次分享。先看我喜欢的万能的老路:获得html内容-> python的工具库解析࿰…...
mysql8查看执行sql历史日志、慢sql历史日志,配置开启sql历史日志general_log、慢sql历史日志slow_query_log
0.本博客sql总结 -- 1.查看参数 -- 1.1.sql日志和慢sql日志输出方式(TABLE/FILE)。global参数 SHOW GLOBAL VARIABLES LIKE log_output; -- 1.2.sql日志开关。global参数 SHOW GLOBAL VARIABLES LIKE general_log%; -- 1.3.慢sql日志开关。global参数 SHOW GLOBAL VARIABLE…...
vscode关闭绑定元素“xxx”隐式具有“any”类型这类错误
在ts的项目里面,真的经常看到any类型的报错,真的很烦的 所以为了眼不见心不乱,我决定消除这个错误提示 在tsconfig.json里面配置 "noImplicitAny": false 就可以了 {"compilerOptions": {"target": "E…...
View绘制流程-Window创建
前言: View绘制流程中,主要流程是这样的: 1.用户进入页面,首先创建和绑定Window; 2.首次创建以及后续vsync信号来临时,会请求执行刷新流程; 3.刷新流程完成后,会通知SurfaceFlin…...
Jenkins build包时虽然单元测试失败了,但是仍然可以成功build包(最终结束时build success)
1.尝试方案1: 尽管单元测试失败,Jenkins Maven仍然可以获得成功-Java 学习之路 将 -Dmaven.test.failure.ignorefalse 添加到 MAVEN_OPTS artifactoryMaven {goals "-U clean install -Dmaven.test.skipfalse -DallowSnapshotstrue -Dmaven.te…...
电力系统稳定器与静态无功补偿器联合提升暂态稳定性Simulink仿真模型研究
使用电力系统稳定器(PSS)和静态无功补偿器(SVC)提高暂态稳定性的simulink仿真模型电力系统这玩意儿最怕的就是突然来个大扰动,比如短路故障或者大负荷切换。这时候发电机的功角曲线要是收不住,分分钟全网停…...
OpenClaw与Qwen3-14B深度优化:降低30%Token消耗的5个技巧
OpenClaw与Qwen3-14B深度优化:降低30%Token消耗的5个技巧 1. 为什么需要关注Token消耗? 当我第一次在本地部署OpenClaw对接Qwen3-14B模型时,就被它惊人的Token消耗量震惊了。一个简单的"打开浏览器搜索天气"任务,竟然…...
嵌入式系统接口技术详解与应用实践
1. 嵌入式系统接口技术概述在嵌入式系统开发中,接口技术是连接处理器与外部设备的关键桥梁。作为一名嵌入式开发工程师,我经常需要根据项目需求选择合适的接口方案。本文将基于多年实战经验,深入解析各类嵌入式接口的工作原理、应用场景和选型…...
FC-CLIP实战:为什么说“卷积不死”?在开放词汇分割中冻结CLIP主干的深度解析与避坑指南
FC-CLIP技术解析:卷积架构在开放词汇分割中的不可替代性 当整个计算机视觉领域似乎都被Transformer架构席卷时,FC-CLIP论文却掷地有声地宣告"卷积不死"。这个看似反潮流的结论背后,隐藏着哪些被忽视的视觉归纳偏置?冻结…...
GraphRAG大模型在药物发现中玩出新花样!揭秘潜在知识图谱的惊人能力!
本文深入探讨了Microsoft GraphRAG在药物发现领域的应用,通过构建科学文献的潜在知识图谱,测试了其检索和合成能力。实验揭示了LLM在处理复杂查询中的优势与局限,强调了语料质量和LLM选择的重要性。GraphRAG展现了高效从非结构化数据中提取洞…...
别再买成品了!手把手教你用立创EDA复刻TP4056充电板,成本不到3块钱
3元自制18650充电器:立创EDA复刻TP4056全流程实战 每次看到抽屉里闲置的18650电池,总想给它们配个充电器,但市面上的成品要么价格虚高,要么功能过剩。作为一个常年折腾电子制作的爱好者,我发现用立创EDA复刻TP4056充电…...
手把手教你用FBRT-YOLO在VisDrone数据集上跑出SOTA:从环境配置到模型推理的保姆级教程
手把手教你用FBRT-YOLO在VisDrone数据集上跑出SOTA:从环境配置到模型推理的保姆级教程 航拍图像目标检测一直是计算机视觉领域的难点,尤其是小目标检测问题。无人机拍摄的图像分辨率高、目标密集且尺寸小,传统检测算法往往难以兼顾精度和速度…...
系统级音频均衡器如何提升macOS音质:开源eqMac完全指南
系统级音频均衡器如何提升macOS音质:开源eqMac完全指南 【免费下载链接】eqMac macOS System-wide Audio Equalizer & Volume Mixer 🎧 项目地址: https://gitcode.com/gh_mirrors/eq/eqMac eqMac是一款开源的macOS系统级音频均衡器与音量混合…...
从滤波到故障诊断:手把手教你用MATLAB实现信号互相关分析的实际项目
从振动信号到故障定位:MATLAB互相关分析的工业实战指南 车间里那台大型离心泵的异常振动已经持续两周了。王工程师带着加速度传感器采集了三组不同位置的振动信号,屏幕上跳动的波形看起来杂乱无章。"到底是轴承磨损还是叶轮不平衡?"…...
告别上位机!纯FPGA实现exFAT文件系统,让你的高速数据直接存成标准文件
纯FPGA实现exFAT文件系统:硬件工程师的高速存储革命 在高速数据采集领域,从雷达信号处理到卫星通信,工程师们长期面临一个核心痛点:如何将海量原始数据高效、可靠地转换为标准文件格式。传统方案依赖上位机或嵌入式处理器进行文件…...
