SpringBoot-集成Minio

news2024/9/25 9:30:25

官方文档:Kubernetes 的 MinIO 对象存储 — MinIO Object Storage for Kubernetes


一、简介

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

二、引入依赖

<!-- MinIO -->
<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>8.2.2</version>
</dependency>
<!-- Hutool -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.18</version>
</dependency>

Tips:MinIO依赖冲突问题 SpringBoot集成MinIO依赖冲突问题_Fly_Camel_Yu的博客-CSDN博客场景一: 使用minio8.3.0版本的依赖,报下列异常:An attempt was made to call a method that does not exist. The attempt was made from the following location: io.minio.S3Base.(S3Base.java:105)The following method did not exist: okhttp3.Requ...https://blog.csdn.net/qq_39974348/article/details/121742672

三、MinIO配置类

import io.minio.MinioClient;
import lombok.SneakyThrows;
import org.atm.dc.app.oss.props.MinioProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;

/**
 * Minio配置类
 *
 * @author meng
 */
@Configuration
@EnableConfigurationProperties(MinioProperties.class)
@ConditionalOnProperty(value = "oss.name", havingValue = "minio")
public class MinioConfiguration {

    @Resource
    private MinioProperties ossProperties;

    @Bean
    @SneakyThrows
    public MinioClient minioClient() {
        return MinioClient.builder()
                .endpoint(ossProperties.getEndpoint())
                .credentials(ossProperties.getAccessKey(), ossProperties.getSecretKey())
                .build();
    }

}

四、MinIO参数配置类

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.List;

/**
 * Minio参数配置类
 *
 * @author meng
 */
@Data
@ConfigurationProperties(prefix = MinioProperties.PREFIX)
public class MinioProperties {

	/**
	 * 配置前缀
	 */
	public static final String PREFIX = "oss";

	/**
	 * 对象存储名称
	 */
	private String name;

	/**
	 * 对象存储服务的URL
	 */
	private String endpoint;

	/**
	 * Access key 账户ID
	 */
	private String accessKey;

	/**
	 * Secret key 密码
	 */
	private String secretKey;

	/**
	 * 默认的存储桶名称
	 */
	private String bucketName = "meng";

	/**
	 * 可上传的文件后缀名
	 */
	private List<String> fileExt;

}

五、参数封装

import lombok.Data;
import java.util.Date;

/**
 * OssFile
 *
 * @author meng
 */
@Data
public class OssFile {
	/**
	 * 文件地址
	 */
	private String filePath;
	/**
	 * 域名地址
	 */
	private String domain;
	/**
	 * 文件名
	 */
	private String name;
	/**
	 * 原始文件名
	 */
	private String originalName;
	/**
	 * 文件hash值
	 */
	public String hash;
	/**
	 * 文件大小
	 */
	private long size;
	/**
	 * 文件上传时间
	 */
	private Date putTime;
	/**
	 * 文件contentType
	 */
	private String contentType;
}

六、MinIO相关方法

import org.atm.dc.app.oss.model.OssFile;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.List;

/**
 * OssTemplate抽象API
 *
 * @author meng
 */
public interface OssTemplate {

    /**
     * 存储桶是否存在
     *
     * @param bucketName 存储桶名称
     * @return boolean
     */
    boolean bucketExists(String bucketName);


    /**
     * 获取文件信息
     *
     * @param fileName 存储桶文件名称
     * @return InputStream
     */
    OssFile getOssInfo(String fileName);

    /**
     * 上传文件
     *
     * @param folderName 上传的文件夹名称
     * @param fileName   上传文件名
     * @param file       上传文件类
     * @return BladeFile
     */
    OssFile upLoadFile(String folderName, String fileName, MultipartFile file);

    /**
     * 上传文件
     *
     * @param folderName 上传的文件夹名称
     * @param fileName   存储桶对象名称
     * @param suffix     文件后缀名
     * @param stream     文件流
     * @return BladeFile
     */
    OssFile upLoadFile(String folderName, String fileName, String suffix, InputStream stream);

    /**
     * 删除文件
     *
     * @param fileName 存储桶对象名称
     */
    boolean removeFile(String fileName);

    /**
     * 批量删除文件
     *
     * @param fileNames 存储桶对象名称集合
     */
    boolean removeFiles(List<String> fileNames);

    /**
     * @Description: 下载文件
     * @Param response: 响应
     * @Param fileName: 文件名
     * @Param filePath: 文件路径
     * @return: void
     */
    void downloadFile(HttpServletResponse response, String fileName, String filePath);
}
MinIOTemplate:
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ObjectUtil;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.DeleteObject;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.atm.dc.app.common.Constants;
import org.atm.dc.app.oss.model.OssFile;
import org.atm.dc.app.oss.props.MinioProperties;
import org.atm.dc.app.oss.template.OssTemplate;
import org.atm.dc.app.util.FileInfoUtil;
import org.atm.dc.exception.BaseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
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.List;
import java.util.stream.Stream;

/**
 * MinIOTemplate
 *
 * @author meng
 */
@Slf4j
@Service
public class MinioTemplate implements OssTemplate {

	private Logger logger = LoggerFactory.getLogger(this.getClass());

	/**
	 * MinIO客户端
	 */
	@Resource
	private MinioClient client;

	/**
	 * 配置类
	 */
	@Resource
	private MinioProperties ossProperties;

	/**
	 * 格式化时间
	 */
	private static final String DATE_FORMAT = "yyyyMMdd";

	/**
	 * 字符集
	 */
	private static final String ENCODING = "UTF-8";

	/**
	 * 存储桶是否存在
	 *
	 * @param bucketName 存储桶名称
	 * @return boolean
	 */
	@Override
	public boolean bucketExists(String bucketName) {
		try {
			return client.bucketExists(BucketExistsArgs.builder().bucket(getBucketName(bucketName)).build());
		} catch (Exception e) {
			logger.error("minio bucketExists Exception:{}", e);
		}
		return false;
	}

	/**
	 * @Description: 创建 存储桶
	 * @Param bucketName: 存储桶名称
	 * @return: void
	 * @Author: wmh
	 * @Date: 2023/8/2 11:28
	 */
	public void makeBucket(String bucketName) {
		try {
			if (!client.bucketExists(BucketExistsArgs.builder().bucket(getBucketName(bucketName)).build())) {
				client.makeBucket(MakeBucketArgs.builder().bucket(getBucketName(bucketName)).build());
				logger.info("minio makeBucket success bucketName:{}", bucketName);
			}
		} catch (Exception e) {
			logger.error("minio makeBucket Exception:{}", e);
		}
	}

	/**
	 * 获取文件信息
	 *
	 * @param fileName 存储桶文件名称
	 * @return InputStream
	 */
	@Override
	public OssFile getOssInfo(String fileName) {
		try {
			StatObjectResponse stat = client.statObject(
					StatObjectArgs.builder().bucket(getBucketName(ossProperties.getBucketName())).object(fileName)
							.build());
			OssFile ossFile = new OssFile();
			ossFile.setName(ObjectUtil.isEmpty(stat.object()) ? fileName : stat.object());
			ossFile.setFilePath(ossFile.getName());
			ossFile.setDomain(getOssHost(ossProperties.getBucketName()));
			ossFile.setHash(String.valueOf(stat.hashCode()));
			ossFile.setSize(stat.size());
			ossFile.setPutTime(DateUtil.date(stat.lastModified().toLocalDateTime()));
			ossFile.setContentType(stat.contentType());
			return ossFile;
		} catch (Exception e) {
			logger.error("minio getOssInfo Exception:{}", e);
		}
		return null;
	}

	/**
	 * 上传文件
	 *
	 * @param folderName 上传的文件夹名称
	 * @param fileName   上传文件名
	 * @param file       上传文件类
	 * @return BladeFile
	 */
	@Override
	@SneakyThrows
	public OssFile upLoadFile(String folderName, String fileName, MultipartFile file) {
		if (file == null || file.isEmpty()) {
			throw new BaseException("400", Constants.FILE_EMPTY);
		}
		// 文件大小
		if (file.getSize() > 5 * 1024 * 1024) {
			throw new BaseException("400", "文件大小不能超过5M");
		}
		String suffix = FileInfoUtil.getFileExtension(file.getOriginalFilename());
		// 文件后缀判断
		if (!CollUtil.contains(ossProperties.getFileExt(), suffix)) {
			String error = String.format("文件类型错误,目前支持[%s]等文件类型",
					String.join(",", ossProperties.getFileExt()));
			throw new BaseException("400", error);
		}
		try {
			return upLoadFile(folderName, fileName, suffix, file.getInputStream());
		} catch (Exception e) {
			logger.error("minio upLoadFile Exception:{}", e);
			throw new BaseException("400", "文件上传失败,请重新上传或联系管理员");
		}
	}

	/**
	 * 上传文件
	 *
	 * @param folderName 上传的文件夹名称
	 * @param fileName   存储桶对象名称
	 * @param suffix     文件后缀名
	 * @param stream     文件流
	 * @return BladeFile
	 */
	@Override
	public OssFile upLoadFile(String folderName, String fileName, String suffix, InputStream stream) {
		try {
			return upLoadFile(ossProperties.getBucketName(), folderName, fileName, suffix, stream,
					"application/octet" + "-stream");
		} catch (Exception e) {
			logger.error("minio upLoadFile Exception:{}", e);
		}
		return null;
	}

	/**
	 * @Description: 上传文件
	 * @Param bucketName: 存储桶名称
	 * @Param folderName: 上传的文件夹名称
	 * @Param fileName: 上传文件名
	 * @Param suffix: 文件后缀名
	 * @Param stream: 文件流
	 * @Param contentType: 文件类型
	 * @Author: wmh
	 * @Date: 2023/8/1 19:59
	 */
	@SneakyThrows
	public OssFile upLoadFile(String bucketName, String folderName, String fileName, String suffix, InputStream stream,
			String contentType) {
		if (!bucketExists(bucketName)) {
			logger.info("minio bucketName is not creat");
			makeBucket(bucketName);
		}
		OssFile file = new OssFile();
		String originalName = fileName;
		String filePath = getFilePath(folderName, fileName, suffix);
		client.putObject(PutObjectArgs.builder().bucket(getBucketName(bucketName)).object(filePath)
				.stream(stream, stream.available(), -1).contentType(contentType).build());
		file.setOriginalName(originalName);
		file.setName(filePath);
		file.setDomain(getOssHost(bucketName));
		file.setFilePath(filePath);
		stream.close();
		logger.info("minio upLoadFile success, filePath:{}", filePath);
		return file;
	}

	/**
	 * 删除文件
	 *
	 * @param fileName 存储桶对象名称
	 */
	@Override
	public boolean removeFile(String fileName) {
		try {
			client.removeObject(
					RemoveObjectArgs.builder().bucket(getBucketName(ossProperties.getBucketName())).object(fileName)
							.build());
			logger.info("minio removeFile success, fileName:{}", fileName);
			return true;
		} catch (Exception e) {
			logger.error("minio removeFile fail, fileName:{}, Exception:{}", fileName, e);
		}
		return false;
	}

	/**
	 * 批量删除文件
	 *
	 * @param fileNames 存储桶对象名称集合
	 */
	@Override
	public boolean removeFiles(List<String> fileNames) {
		try {
			Stream<DeleteObject> stream = fileNames.stream().map(DeleteObject::new);
			client.removeObjects(RemoveObjectsArgs.builder().bucket(getBucketName(ossProperties.getBucketName()))
					.objects(stream::iterator).build());
			logger.info("minio removeFiles success, fileNames:{}", fileNames);
			return true;
		} catch (Exception e) {
			logger.error("minio removeFiles fail, fileNames:{}, Exception:{}", fileNames, e);
		}
		return false;
	}

	/**
	 * @Description: 下载文件
	 * @Param response: 响应
	 * @Param fileName: 文件名
	 * @Param filePath: 文件路径
	 * @return: void
	 * @Author: wmh
	 * @Date: 2023/8/2 14:08
	 */
	@Override
	public void downloadFile(HttpServletResponse response, String fileName, String filePath) {
		GetObjectResponse is = null;
		try {
			GetObjectArgs getObjectArgs =
					GetObjectArgs.builder().bucket(ossProperties.getBucketName()).object(filePath)
					.build();
			is = client.getObject(getObjectArgs);
			// 设置文件ContentType类型,这样设置,会自动判断下载文件类型
			response.setContentType("application/x-msdownload");
			response.setCharacterEncoding(ENCODING);
			// 设置文件头:最后一个参数是设置下载的文件名并编码为UTF-8
			response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, ENCODING));
			IoUtil.copy(is, response.getOutputStream());
			logger.info("minio downloadFile success, filePath:{}", filePath);
		} catch (Exception e) {
			logger.error("minio downloadFile Exception:{}", e);
		} finally {
			IoUtil.close(is);
		}
	}

	/**
	 * 获取文件外链
	 *
	 * @param bucketName bucket名称
	 * @param fileName   文件名称
	 * @param expires    过期时间
	 * @return url
	 */
	public String getPresignedObjectUrl(String bucketName, String fileName, Integer expires) {
		String link = "";
		try {
			link = client.getPresignedObjectUrl(
					GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(getBucketName(bucketName))
							.object(fileName).expiry(expires).build());
		} catch (Exception e) {
			logger.error("minio getPresignedObjectUrl is fail, fileName:{}", fileName);
		}
		return link;
	}

	/**
	 * 根据规则生成存储桶名称规则
	 *
	 * @param bucketName 存储桶名称
	 * @return String
	 */
	private String getBucketName(String bucketName) {
		return bucketName;
	}

	/**
	 * 根据规则生成文件路径
	 *
	 * @param folderName       上传的文件夹名称
	 * @param originalFilename 原始文件名
	 * @param suffix           文件后缀名
	 * @return string 上传的文件夹名称/yyyyMMdd/原始文件名_时间戳.文件后缀名
	 */
	private String getFilePath(String folderName, String originalFilename, String suffix) {
		return StrPool.SLASH + String.join(StrPool.SLASH, folderName, DateUtil.date().toString(DATE_FORMAT),
				originalFilename) + StrPool.C_UNDERLINE + DateUtil.current() + StrPool.DOT + suffix;
	}

	/**
	 * 获取域名
	 *
	 * @param bucketName 存储桶名称
	 * @return String
	 */
	public String getOssHost(String bucketName) {
		return ossProperties.getEndpoint() + StrPool.SLASH + getBucketName(bucketName);
	}

}

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

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

相关文章

Spring Boot和XXL-Job:高效定时任务管理

Spring Boot和XXL-Job&#xff1a;高效定时任务管理 前言第一&#xff1a;XXL-Job简介什么是XXL-job对比别的任务调度 第二&#xff1a; springboot整合XXL-job配置XXL-Job Admin拉取XXL-Job代码修改拉取的配置 配置执行器自己的项目如何整合maven依赖properties文件配置执行器…

工厂干洗店洗鞋店系统,校园洗护小程序来了

洗鞋店小程序&#xff0c;干洗店软件&#xff0c;洗护行业小程序,上门取衣小程序,预约干洗小程序,校园干洗店小程序,工厂干洗店小程序,干洗店小程序开发&#xff0c;成品软件开发 洗衣工厂软件、功能强大&#xff01; 包含以下主要功能&#xff1a; * 用户选择洗护用品&#x…

Azure - 机器学习:创建机器学习所需资源,配置工作区

目录 一、Azure机器学习工作区与计算实例简要介绍工作区计算实例 二、创建工作区1. 登录到 Azure 机器学习工作室2. 选择“创建工作区”3. 提供以下信息来配置新工作区&#xff1a;4. 选择“创建”以创建工作区 三、创建计算实例四、工作室实战4.1 工作室快速导览4.2 从示例笔记…

CV算法工程师的LLM日志(1)微调技术——即插即用的neft-Tune【原理代码】

CV算法的LLM领域日志 目前维护的CV方向开源项目暂时暂停&#xff0c;原因是现在在做LLM方向的研发工作&#xff0c;所以需要时间消化前沿技术和总结经验&#xff0c;最近看到了一个非常简单的LLM训练Trick 分享一下&#xff0c;后续会逐渐把自己使用的一些LLM范式技术原理和代…

部分背包问题细节(贪心)

有一种情况是&#xff0c;背包可以把金币全部拿走&#xff1a; 如果num小于0则返回值

重症医学科常用评估量表汇总,建议收藏!

根据重症医学科医生的量表使用情况&#xff0c;笔者整理了10个重症医学科常用量表&#xff0c;可在线评测直接出结果&#xff0c;可转发使用&#xff0c;可生成二维码使用&#xff0c;可创建项目进行数据管理&#xff0c;有需要的小伙伴赶紧收藏&#xff01; 简明急性生理功能评…

Rocksdb LSM Tree Compaction策略

RocksDB读写简介 直接画图说明。这张图取自Flink PMC大佬Stefan Richter在Flink Forward 2018演讲的PPT&#xff0c;笔者重画了一下。 RocksDB的写缓存&#xff08;即LSM树的最低一级&#xff09;名为memtable&#xff0c;对应HBase的MemStore&#xff1b;读缓存名为block cac…

基于QT的图书管理系统

获取代码&#xff1a; 知识付费时代&#xff0c;低价有偿获取代码&#xff0c;请理解&#xff01; (1) 下载链接: 后发(2) 添加博主微信获取&#xff08;有偿&#xff09;,备注来源: mryang511688(3) 快速扫码咨询&#xff1a; 项目描述 技术&#xff1a;C、QT等 摘要&#…

(1)(1.8) Hondex声纳

文章目录 前言 1 推荐的硬件 2 连接和配置 3 参数说明 前言 Hondex HE-8S 是一款回声测深仪&#xff08;又称水下声纳&#xff09;&#xff0c;测深范围 100m&#xff0c;内置 GPS 和 NMEA 输出&#xff0c;可由 ArduPilot 使用。其他 Hondex 声纳也可以使用&#xff0c;但…

redis学习(三)——java整合redis

Jedis Jedis可以用于java连接redis数据库 新建一个maven项目&#xff0c;导入Jedis依赖 <dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>RELEASE</version><scope>test…

mcgsTpc屏与施耐德TM218PLC通讯说明

一、 硬件连接 1、PLC通讯接口说明&#xff1a; 2、通讯电缆图&#xff1a; 二、PLC设置 1. 配置端口&#xff1a; 双击串行线路—弹出右侧设置窗口---设置串口通讯参数 2. 添加MODBUS协议。 ① 右击串口线路&#xff0c;选择添加设备&#xff1a; ② 选择现场总线&#xf…

Studio One6.5中文版本更新下载

供所有 Studio One 6 和 Studio 用户下载&#xff0c;以下是部分改进&#xff01; Studio One 6.5 更新功能&#xff1a; 空间音频制作工作流程与杜比全景声集成 支持杜比全景声双耳耳机监听 字幕和功能铺增强功能 整合杜比全景声的空间音频制作工作流程 Studio One 6.5…

损失函数总结(三):BCELoss、CrossEntropyLoss

损失函数总结&#xff08;三&#xff09;&#xff1a;BCELoss、CrossEntropyLoss 1 引言2 损失函数2.1 BCELoss2.2 CrossEntropyLoss 3 总结 1 引言 在前面的文章中已经介绍了介绍了一系列损失函数 (L1Loss、MSELoss)。在这篇文章中&#xff0c;会接着上文提到的众多损失函数继…

关于路由转发

路由表的作用 路由表的作用&#xff1a;目标网络匹配路由表&#xff0c;从相应网络转发&#xff1b;不匹配路由表&#xff0c;丢弃或转发至默认路由器。 路由转发的原理 根据IP地址找到目标网络&#xff0c;由应路由器解封装查看目标网络是否可达&#xff0c;重新封装进行转…

什么是数据中心的测试负载?

数据中心的测试负载是指在数据中心环境中进行的负载测试&#xff0c;以评估数据中心的性能、可靠性和可扩展性。负载测试是通过模拟实际使用情况&#xff0c;向数据中心的系统和组件施加各种类型的负载&#xff0c;以确定其在不同负载条件下的表现和响应能力。 通过模拟高负载情…

GCE的安装和使用

GCE的安装和使用 GCE的安装使用1. GCE的安装2. GCE的使用补充&#xff1a;一个简单的R脚本——kmerpdf.R&#xff0c;用于绘制kmer的种类和数量分布图 GCE的安装使用 一个基因组评估软件。其他同类型软件Genomescope 1. GCE的安装 Github官网&#xff1a;https://github.com…

css-渐变色矩形

效果图&#xff1a; 代码&#xff1a; html: <!DOCTYPE html> <html><head><meta charset"utf-8"><meta name"viewport" content"initial-scale1.0, user-scalableno" /><title></title><link …

一文讲解电源技术中专为准谐振转换器供电 高性能电流模式控制器NCP1380BDR2G

NCP1380BDR2G是一款高性能器件&#xff0c;旨在为准谐振转换器供电。该控制器基于专属的谷锁闭系统&#xff0c;可以在功率负载变轻时进行切换并降低开关频率。这样将产生稳定的运行&#xff0c;即使在漏极-源极谷中总是触发的开关事件下也是如此。此系统可在低至第 4 个谷的条…

Zip密码忘记了,如何破解密码?

Zip压缩包设置了密码&#xff0c;解压的时候就需要输入正确对密码才能顺利解压出文件&#xff0c;正常当我们解压文件或者删除密码的时候&#xff0c;虽然方法多&#xff0c;但是都需要输入正确的密码才能完成。忘记密码就无法进行操作。 那么&#xff0c;忘记了zip压缩包的密…

YOLOv5 添加 OTA,并使用 coco、CrowdHuman数据集进行训练。

YOLO-OTA 第一步&#xff1a;拉取 YOLOv5 的代码第二步&#xff1a;添加 ComputeLossOTA 函数第二步&#xff1a;修改 train 和 val 中损失函数为 ComputeLossOTA 函数1、在 train.py 中 首先添加 ComputeLossOTA 库。2、在 train.py 修改初始化的损失函数3、在 train.py 修改一…