行业知识
SpringBoot-集成Minio
2026-11-12 08:44:26
来源:互联网转载
官方文档:Kubernetes 的 MinIO 对象存储 — MinIO Object Storage for Kubernetes
一、简介
Minio 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。Minio是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或者 MySQL。
二、引入依赖
io.minio minio 8.2.2 cn.hutool hutool-all 5.8.18
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.meng.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 fileExt;} yml文件:
#MINIOoss: name: minio accessKey: 123456 secretKey: 123456 bucketName: test endpoint: http://ip:端口号 fileExt: png,jpg,jpeg,pdf
五、参数封装
import lombok.Data;import java.util.Date;/** * OssFile * * @author meng */@Datapublic 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.meng.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 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.meng.oss.model.OssFile;import org.meng.oss.props.MinioProperties;import org.meng.oss.template.OssTemplate;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@Servicepublic class MinioTemplate implements OssTemplate {private Logger logger = LoggerFactory.getLogger(this.getClass());/** * MinIO客户端 */@Resourceprivate MinioClient client;/** * 配置类 */@Resourceprivate MinioProperties ossProperties;/** * 格式化时间 */private static final String DATE_FORMAT = "yyyyMMdd";/** * 字符集 */private static final String ENCODING = "UTF-8";/** * 存储桶是否存在 * * @param bucketName 存储桶名称 * @return boolean */@Overridepublic 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 * @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 */@Overridepublic 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@SneakyThrowspublic OssFile upLoadFile(String folderName, String fileName, MultipartFile file) {if (file == null || file.isEmpty()) {throw new RuntimeException("400", "文件不能为空");}// 文件大小if (file.getSize() > 5 * 1024 * 1024) {throw new RuntimeException("400", "文件大小不能超过5M");}String suffix = getFileExtension(file.getOriginalFilename());// 文件后缀判断if (!CollUtil.contains(ossProperties.getFileExt(), suffix)) {String error = String.format("文件类型错误,目前支持[%s]等文件类型",String.join(",", ossProperties.getFileExt()));throw new RuntimeException("400", error);}try {return upLoadFile(folderName, fileName, suffix, file.getInputStream());} catch (Exception e) {logger.error("minio upLoadFile Exception:{}", e);throw new RuntimeException("400", "文件上传失败,请重新上传或联系管理员");}} /** * 获取文件后缀名 * * @param fullName 文件全名 * @return {String} */ public static String getFileExtension(String fullName) { Assert.notNull(fullName, "minio file fullName is null."); String fileName = new File(fullName).getName(); int dotIndex = fileName.lastIndexOf('.'); return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1); }/** * 上传文件 * * @param folderName 上传的文件夹名称 * @param fileName 存储桶对象名称 * @param suffix 文件后缀名 * @param stream 文件流 * @return BladeFile */@Overridepublic 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 */@SneakyThrowspublic 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 存储桶对象名称 */@Overridepublic 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 存储桶对象名称集合 */@Overridepublic boolean removeFiles(List fileNames) {try {Stream 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 */@Overridepublic 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-8response.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);}}
上一篇:斗鱼服务器崩溃怎么回事?
下一篇:饥荒正版服务器怎么设?