SpringBoot集成对象存储服务Minio

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

环境搭建

docker-compose搭建


# 可参考 https://docs.min.io/docs/minio-docker-quickstart-guide.html
version: '3'
services:minio:image: minio/minio:latest                                    # 原镜像`minio/minio:latest`container_name: minio                                        # 容器名为'minio'restart: unless-stopped                                              # 指定容器退出后的重启策略为始终重启,但是不考虑在Docker守护进程启动时就已经停止了的容器volumes:                                                     # 数据卷挂载路径设置,将本机目录映射到容器目录- "./minio/data:/data"- "./minio/minio:/minio"- "./minio/config:/root/.minio"environment:                                      # 设置环境变量,相当于docker run命令中的-eTZ: Asia/ShanghaiLANG: en_US.UTF-8MINIO_PROMETHEUS_AUTH_TYPE: "public"MINIO_ACCESS_KEY: "root"                        # 登录账号MINIO_SECRET_KEY: "password"                    # 登录密码command: server /data  --console-address ":9001"logging:driver: "json-file"options:max-size: "100m"ports:                              # 映射端口- "9000:9000" # 文件上传&预览端口- "9001:9001" # 控制台访问端口

启动服务:docker-compose -f docker-compose-minio.yml -p minio up -d

访问地址:ip地址:9001/minio 登录账号密码:root/password
在这里插入图片描述
一、依赖


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>springboot-demo</artifactId><groupId>com.et</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>minio</artifactId><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.20</version></dependency><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.2.2</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.11</version></dependency></dependencies>
</project>

二、配置文件和启动类

三、Config配置类

@Data
@Configuration
@ConfgurationProperties(prefix = "minio")
public class MinioConfig{private String endpoint;private String accessKey;private String secretKey;private String bucketName;@Beanpublic MinioClient minioClient() {return MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();}
}

四、MinioUtil工具

@Slf4j
@Component
@RequiredArgsConstructor
public class MinioUtils {private final MinioClient minioClient;/*** 启动SpringBoot容器的时候初始化Bucket* 如果没有Bucket则创建** @param bucketName*/@SneakyThrows(Exception.class)private void createBucket(String bucketName) {if (!bucketExists(bucketName)) {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());}}/*** 判断Bucket是否存在,true:存在,false:不存在** @param bucketName* @return*/@SneakyThrows(Exception.class)public boolean bucketExists(String bucketName) {return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());}/*** 获得Bucket的策略** @param bucketName* @return*/@SneakyThrows(Exception.class)public String getBucketPolicy(String bucketName) {return minioClient.getBucketPolicy(GetBucketPolicyArgs.builder().bucket(bucketName).build());}/*** 获得所有Bucket列表** @return*/@SneakyThrows(Exception.class)public List<Bucket> getAllBuckets() {return minioClient.listBuckets();}/*** 根据bucketName获取其相关信息** @param bucketName* @return*/@SneakyThrows(Exception.class)public Optional<Bucket> getBucket(String bucketName) {return getAllBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();}/*** 根据bucketName删除Bucket,true:删除成功; false:删除失败,文件或已不存在** @param bucketName* @throws Exception*/@SneakyThrows(Exception.class)public void removeBucket(String bucketName) {minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());}/*** 判断文件是否存在** @param bucketName* @param objectName* @return*/public boolean isObjectExist(String bucketName, String objectName) {boolean exist = true;try {minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());} catch (Exception e) {log.error("[Minio工具类]>>>> 判断文件是否存在, 异常:", e);exist = false;}return exist;}/*** 判断文件夹是否存在** @param bucketName* @param objectName* @return*/public boolean isFolderExist(String bucketName, String objectName) {boolean exist = false;try {Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(objectName).recursive(false).build());for (Result<Item> result : results) {Item item = result.get();if (item.isDir() && objectName.equals(item.objectName())) {exist = true;}}} catch (Exception e) {log.error("[Minio工具类]>>>> 判断文件夹是否存在,异常:", e);exist = false;}return exist;}/*** 根据文件前置查询文件** @param bucketName 存储桶* @param prefix     前缀* @param recursive  是否使用递归查询* @return MinioItem 列表*/@SneakyThrows(Exception.class)public List<Item> getAllObjectsByPrefix(String bucketName,String prefix,boolean recursive) {List<Item> list = new ArrayList<>();Iterable<Result<Item>> objectsIterator = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(recursive).build());if (objectsIterator != null) {for (Result<Item> o : objectsIterator) {Item item = o.get();list.add(item);}}return list;}/*** 获取文件流** @param bucketName 存储桶* @param objectName 文件名* @return 二进制流*/@SneakyThrows(Exception.class)public InputStream getObject(String bucketName, String objectName) {return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());}/*** 断点下载** @param bucketName 存储桶* @param objectName 文件名称* @param offset     起始字节的位置* @param length     要读取的长度* @return 二进制流*/@SneakyThrows(Exception.class)public InputStream getObject(String bucketName, String objectName, long offset, long length) {return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).offset(offset).length(length).build());}/*** 获取路径下文件列表** @param bucketName 存储桶* @param prefix     文件名称* @param recursive  是否递归查找,false:模拟文件夹结构查找* @return 二进制流*/public Iterable<Result<Item>> listObjects(String bucketName, String prefix, boolean recursive) {return minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(recursive).build());}/*** 使用MultipartFile进行文件上传** @param bucketName  存储桶* @param file        文件名* @param objectName  对象名* @param contentType 类型* @return*/@SneakyThrows(Exception.class)public ObjectWriteResponse uploadFile(String bucketName, MultipartFile file, String objectName, String contentType) {InputStream inputStream = file.getInputStream();return minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).contentType(contentType).stream(inputStream, inputStream.available(), -1).build());}/*** 图片上传* @param bucketName* @param imageBase64* @param imageName* @return*/public ObjectWriteResponse uploadImage(String bucketName, String imageBase64, String imageName) {if (!StringUtils.isEmpty(imageBase64)) {InputStream in = base64ToInputStream(imageBase64);String newName = System.currentTimeMillis() + "_" + imageName + ".jpg";String year = String.valueOf(new Date().getYear());String month = String.valueOf(new Date().getMonth());return uploadFile(bucketName, year + "/" + month + "/" + newName, in);}return null;}public static InputStream base64ToInputStream(String base64) {ByteArrayInputStream stream = null;try {byte[] bytes = new BASE64Decoder().decodeBuffer(base64.trim());stream = new ByteArrayInputStream(bytes);} catch (Exception e) {e.printStackTrace();}return stream;}/*** 上传本地文件** @param bucketName 存储桶* @param objectName 对象名称* @param fileName   本地文件路径* @return*/@SneakyThrows(Exception.class)public ObjectWriteResponse uploadFile(String bucketName, String objectName, String fileName) {return minioClient.uploadObject(UploadObjectArgs.builder().bucket(bucketName).object(objectName).filename(fileName).build());}/*** 通过流上传文件** @param bucketName  存储桶* @param objectName  文件对象* @param inputStream 文件流* @return*/@SneakyThrows(Exception.class)public ObjectWriteResponse uploadFile(String bucketName, String objectName, InputStream inputStream) {return minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(inputStream, inputStream.available(), -1).build());}/*** 创建文件夹或目录** @param bucketName 存储桶* @param objectName 目录路径* @return*/@SneakyThrows(Exception.class)public ObjectWriteResponse createDir(String bucketName, String objectName) {return minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(new ByteArrayInputStream(new byte[]{}), 0, -1).build());}/*** 获取文件信息, 如果抛出异常则说明文件不存在** @param bucketName 存储桶* @param objectName 文件名称* @return*/@SneakyThrows(Exception.class)public String getFileStatusInfo(String bucketName, String objectName) {return minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build()).toString();}/*** 拷贝文件** @param bucketName    存储桶* @param objectName    文件名* @param srcBucketName 目标存储桶* @param srcObjectName 目标文件名*/@SneakyThrows(Exception.class)public ObjectWriteResponse copyFile(String bucketName, String objectName, String srcBucketName, String srcObjectName) {return minioClient.copyObject(CopyObjectArgs.builder().source(CopySource.builder().bucket(bucketName).object(objectName).build()).bucket(srcBucketName).object(srcObjectName).build());}/*** 删除文件** @param bucketName 存储桶* @param objectName 文件名称*/@SneakyThrows(Exception.class)public void removeFile(String bucketName, String objectName) {minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());}/*** 批量删除文件** @param bucketName 存储桶* @param keys       需要删除的文件列表* @return*/public void removeFiles(String bucketName, List<String> keys) {List<DeleteObject> objects = new LinkedList<>();keys.forEach(s -> {objects.add(new DeleteObject(s));try {removeFile(bucketName, s);} catch (Exception e) {log.error("[Minio工具类]>>>> 批量删除文件,异常:", e);}});}/*** 获取文件外链** @param bucketName 存储桶* @param objectName 文件名* @param expires    过期时间 <=7 秒 (外链有效时间(单位:秒))* @return url*/@SneakyThrows(Exception.class)public String getPresignedObjectUrl(String bucketName, String objectName, Integer expires) {GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder().expiry(expires).bucket(bucketName).object(objectName).build();return minioClient.getPresignedObjectUrl(args);}/*** 获得文件外链** @param bucketName* @param objectName* @return url*/@SneakyThrows(Exception.class)public String getPresignedObjectUrl(String bucketName, String objectName) {GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(objectName).method(Method.GET).build();return minioClient.getPresignedObjectUrl(args);}/*** 将URLDecoder编码转成UTF8** @param str* @return* @throws UnsupportedEncodingException*/public String getUtf8ByURLDecoder(String str) throws UnsupportedEncodingException {String url = str.replaceAll("%(?![0-9a-fA-F]{2})", "%25");return URLDecoder.decode(url, "UTF-8");}
}

五、Controller接口

@Slf4j
@RestController
@RequestMapping("/oss")
public class OSSController {@Autowiredprivate MinioUtils minioUtils;@Autowiredprivate MinioConfig minioConfig;/*** 文件上传** @param file*/@PostMapping("/upload")public String upload(@RequestParam("file") MultipartFile file) {try {//文件名String fileName = file.getOriginalFilename();String newFileName = System.currentTimeMillis() + "." + StringUtils.substringAfterLast(fileName, ".");//类型String contentType = file.getContentType();minioUtils.uploadFile(minioConfig.getBucketName(), file, newFileName, contentType);return "上传成功";} catch (Exception e) {e.printStackTrace();log.error("上传失败");return "上传失败";}}/*** 删除** @param fileName*/@DeleteMapping("/")public void delete(@RequestParam("fileName") String fileName) {minioUtils.removeFile(minioConfig.getBucketName(), fileName);}/*** 获取文件信息** @param fileName* @return*/@GetMapping("/info")public String getFileStatusInfo(@RequestParam("fileName") String fileName) {return minioUtils.getFileStatusInfo(minioConfig.getBucketName(), fileName);}/*** 获取文件外链** @param fileName* @return*/@GetMapping("/url")public String getPresignedObjectUrl(@RequestParam("fileName") String fileName) {return minioUtils.getPresignedObjectUrl(minioConfig.getBucketName(), fileName);}/*** 文件下载** @param fileName* @param response*/@GetMapping("/download")public void download(@RequestParam("fileName") String fileName, HttpServletResponse response) {try {InputStream fileInputStream = minioUtils.getObject(minioConfig.getBucketName(), fileName);response.setHeader("Content-Disposition", "attachment;filename=" + fileName);response.setContentType("application/force-download");response.setCharacterEncoding("UTF-8");IOUtils.copy(fileInputStream, response.getOutputStream());} catch (Exception e) {log.error("下载失败");}}
}

测试:

我们使用postman来测试一下上传接口测试成功了

在这里插入图片描述

也可以在登录minio后台,查看我们刚刚上传的文件
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/742554.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IP数据报格式

每一行都由32位比特&#xff0c;即4个字节组成&#xff0c;每个格子称为字段或者域。IP数据报由20字节的固定部分和最大40字节的可变部分组成。 总长度 总长度为16个比特&#xff0c;该字段的取值以字节为单位&#xff0c;用来表示IPv4数据报的长度(首部长度数据载荷长度)最大…

基于java+springboot+vue实现的停车场管理系统(文末源码+Lw)23-258

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统停车场管理系统信息管理难度大&#xff0c;容错率低&…

【Axure高保真原型】下拉列表切换图表

今天和大家分享通过下拉列表动态切换统计图表的原型模板&#xff0c;我们可以通过下拉列表选择要显示的图表&#xff0c;包括柱状图、条形图、饼图、环形图、折线图、曲线图、面积图、阶梯图、雷达图&#xff1b;而且图表数据可以在左侧表格中动态维护&#xff0c;包括增加修改…

《你是什么垃圾-弹幕版》

你是什么垃圾-弹幕版 类型&#xff1a;垃圾分类 视角&#xff1a;2d 乐趣点&#xff1a;弹幕交互&#xff0c;热点追踪 时间&#xff1a;2021 个人职责&#xff1a; 所有程序部分的设计开发 此游戏是某个早晨&#xff0c;在早点铺子吃米线的时候构思出来的。当时正是&#xff0…

bpmn-js系列之Viewer

上一篇文章『bpmn-js系列之Modeler、以及流程编辑界面的优化』介绍了bpmn-js的modeler模式下的一些开发配置&#xff0c;这篇文章将会介绍Viewer模式的使用 以下演示代码基于上一节搭建好的vue环境&#xff0c;使用bpmn版本为当前最新版7.3.0 基本使用 Viewer的使用与Modele…

【基础CSS】

本文章属于学习笔记&#xff0c;在https://www.freecodecamp.org/chinese/learn/2022/responsive-web-design/中练习 二、 CSS 样式&#xff0c;新建一个文件.css&#xff0c;该文件不含有style标签 <style>. h1&#xff0c;h2&#xff0c;p{ text-align&#xff1a;ce…

Android 优化

布局优化: 减少布局层级&#xff0c;使用ViewStub提高显示速度&#xff0c;布局服用&#xff0c;尽可能少使用warp_content&#xff0c;删除空间中无用的属性,避免过度绘制移除window默认背景&#xff0c;按需显示展位图&#xff0c;自定义View优化&#xff0c;使用canvas.clip…

mysql57开启biglog并查看biglog保姆级教程

目录 详细步骤 1.修改mysql配置文件 2.重启mysql 3.操作数据库 4.查看binglog 4.1mysqlbinlog示例: 4.2mysqlbinlog将biglog写入文件示例: 4.2.1biglog中创建表示例: 4.2.2插入示例 4.2.3更新示例 详细步骤 1.修改mysql配置文件 vi /etc/my.cnf [mysqld]下增加如下…

Skywalking(9.7.0) 告警配置

图片被吞&#xff0c;来这里看吧&#xff1a;https://juejin.cn/post/7344567669893021736 过年前一天发版&#xff0c;大家高高兴兴准备回家过年去了。这时候老板说了一句&#xff0c;记得带上电脑&#xff0c;关注用户反馈。有紧急问题在高速上都得给我找个服务区改好。 但是…

C++初阶

1.缺省参数 给缺省参数的时候&#xff0c;不能声明&#xff0c;定义同时给&#xff0c;只能声明的时候给缺省参数&#xff0c;同时给程序报错&#xff1b; 2.函数重载 C语言不允许同名函数的存在&#xff0c;函数名不能相同&#xff0c;C引入函数重载&#xff0c;函数名可以…

IDEA启动时,电脑非常的卡

选择Help -> Change memory Settings 把启动内存调大一点就行了&#xff0c;反正要超过你平时使用IDEA时使用到的内存大小就行。 原因解释&#xff1a; JVM在运行时会回收新生代和老年代的垃圾&#xff0c;新生代无法回收的对象&#xff0c;比如&#xff1a;回收15次都没有…

什么是递归组件?

递归组件是指在Vue.js中&#xff0c;组件内部直接或间接地引用自身的情况。通过递归组件&#xff0c;可以实现树形结构、评论列表、菜单等具有层级关系的数据展示。 在使用递归组件时&#xff0c;需要注意以下几点&#xff1a; 定义方式&#xff1a;在Vue组件中&#xff0c;可…

2024华为OD机考面试经验分享

什么是华为OD&#xff1f; 华为OD因为还是外包性质&#xff0c;相对正编来说要轻松一些。&#xff08;前提是你不想去卷A绩效&#xff09;&#xff0c;一般情况可以八九点就溜下班。然后工作环境的话&#xff0c;和正编一样。 很多小伙伴其实都是从大城市想回老家&#xff0c…

希尔排序的应用场景

下面是一个简单的希尔排序的代码实现&#xff0c;使用Python语言&#xff1a; def shell_sort(arr):n len(arr)gap n // 2 # 初始化间隔# 间隔逐渐减小while gap > 0:for i in range(gap, n):temp arr[i]j i# 插入排序的步骤while j > gap and arr[j - gap] > t…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的农作物害虫检测系统(深度学习模型+UI界面+训练数据集)

摘要&#xff1a;开发农作物害虫检测系统对于提高农业生产效率和作物产量具有关键作用。本篇博客详细介绍了如何运用深度学习构建一个农作物害虫检测系统&#xff0c;并提供了完整的实现代码。该系统基于强大的YOLOv8算法&#xff0c;并对比了YOLOv7、YOLOv6、YOLOv5&#xff0…

密码校验规则(不能包含3个及以上字典、键盘连续字符)

需求 1、长度大于8&#xff0c;且小于32 2、不能包含用户名 3、不能包含连续3位及以上相同字母或数字 4、不能包含3个及以上字典连续字符 4、数字、小写字母、大写字母、特殊字符&#xff0c;至少包含三种 package com.yy.springboottest.util;/*** author ode* create 2024-03…

前端请求到 SpringMVC 的处理流程

1. 发起请求 客户端通过 HTTP 协议向服务器发起请求。 2. 前端控制器&#xff08;DispatcherServlet&#xff09; 这个请求会先到前端控制器 DispatcherServlet&#xff0c;它是整个流程的入口点&#xff0c;负责接收请求并将其分发给相应的处理器。 3. 处理器映射&#xf…

物体检测-系列教程24:YOLOV5 源码解析14 (训练过程解读:训练配置文件、训练脚本train.py)

&#x1f60e;&#x1f60e;&#x1f60e;物体检测-系列教程 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 点我下载源码 18、训练配置文件 位置yolov5/data文件夹/hyp.scratch.yaml文件 文件内容&#xff1a; …

SpringBoot项目中出现不同端口跨域问题,如何解决?

方法一&#xff1a;比较繁琐&#xff0c;适合少量Controller控制器类 方法二 &#xff1a;需要写一个全局的配置文件即可 在如图所示的common目录下新建一个CorsConfig的class文件 具体代码展示&#xff1a; import org.springframework.context.annotation.Bean; import o…

前端Vue列表组件 list组件:实现高效数据展示与交互

前端Vue列表组件 list组件&#xff1a;实现高效数据展示与交互 摘要&#xff1a;在前端开发中&#xff0c;列表组件是展示数据的重要手段。本文将介绍如何使用Vue.js构建一个高效、可复用的列表组件&#xff0c;并探讨其在实际项目中的应用。 效果图如下&#xff1a; 一、引言…