SpringBoot接入MinIo
-
添加
pom.xml
依赖<dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>8.3.3</version> </dependency>
-
配置
yml
参数# MinIO 对象存储配置 minio: # 是否开启 enabled: true # 服务地址 url: http://192.168.1.139:7101 # 账号 access: archive # 密钥 secret: jiyang123!@# # 存储桶名称 bucket: files
-
客户端初始化配置
import io.minio.BucketExistsArgs; import io.minio.MakeBucketArgs; import io.minio.MinioClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * MinIO对象存储配置类 * * @author wujie * @version 1.0 */ @ConditionalOnProperty(name = "minio.enabled", havingValue = "false") @Configuration public class MinioConfig { private static final Logger log = LoggerFactory.getLogger(MinioConfig.class); @Value("${minio.url}") private String url; @Value("${minio.access}") private String access; @Value("${minio.secret}") private String secret; @Value("${minio.bucket}") private String bucket; /** * MinIO对象存储系统配置 * * @return {@link MinioClient} */ @Bean public MinioClient minioClient() { log.info("-----------MinIO对象存储初始化加载-----------"); log.info("{}-----{}------{}", url, access, bucket); MinioClient minioClient = null; try { minioClient = MinioClient.builder().endpoint(url).credentials(access, secret).build(); // 判断Bucket是否存在 boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build()); if (!exists) { // 不存在则创建一个新的Bucket minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build()); log.info("-----------MinIO对象存储Bucket[{}]已创建-----------", bucket); } log.info("-----------MinIO对象存储初始化完成-----------"); } catch (Exception e) { log.error("MinIO对象存储初始化失败,{}", e.getMessage()); } return minioClient; } }
-
工具类
import com.jiyang.common.exception.CustomException; import io.minio.*; import io.minio.http.Method; import io.minio.messages.DeleteError; import io.minio.messages.DeleteObject; import io.minio.messages.Item; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.http.MediaType; import org.springframework.http.MediaTypeFactory; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.io.InputStream; import java.util.*; import java.util.concurrent.TimeUnit; /** * MinIO对象存储工具类 * * @author wujie * @version 1.0 */ @ConditionalOnProperty(name = "minio.enabled", havingValue = "false") @Component public class MinioUtils { private static final Logger log = LoggerFactory.getLogger(MinioUtils.class); @Value("${minio.bucket}") private String bucket; private final MinioClient minioClient; @Autowired(required = false) public MinioUtils(MinioClient minioClient) { this.minioClient = minioClient; } /** * 获取日期字符串,用分隔符模拟文件夹路径 */ private static final String NOW = DateUtils.parseDateToStr("yyyy/MM/dd/", new Date()); /** * 文件对象上传 * * @param file 文件对象 * @return 存储的文件名和路径 */ public String putObject(MultipartFile file) { try { return putObject(file.getInputStream(), file.getResource().getFilename(), file.getContentType()); } catch (IOException e) { throw new CustomException("MinIO文件上传失败," + e.getMessage()); } } /** * 文件流存储 * <p> * 添加的Object大小不能超过 5GB。 * 默认情况下,如果已存在同名Object且对该Object有访问权限,则新添加的Object将覆盖原有的Object * OSS没有文件夹的概念,所有资源都是以文件来存储,但您可以通过创建一个以正斜线(/)结尾,大小为 0的Object来创建模拟文件夹(指定 /后,默认会自动创建) * * @param is 文件流 * @param fileName 文件名称 * @param contentType 文件类型 * @return 存储的文件名和路径 */ public String putObject(InputStream is, String fileName, String contentType) { try { String name = NOW + fileName; minioClient.putObject( PutObjectArgs.builder().bucket(bucket).object(name).stream( is, is.available(), -1) .contentType(contentType) .build() ); log.info("MinIO文件[{}]存储成功", name); return name; } catch (Exception e) { throw new CustomException("MinIO文件存储失败," + e.getMessage()); } finally { IOUtils.closeQuietly(is); } } /** * 本地磁盘文件存储 * * @param objectName 对象名称 * @param fileName 文件名,带磁盘路径 * @return 存储的文件名和路径 */ public String uploadObject(String objectName, String fileName) { try { String name = NOW + objectName; String content = MediaTypeFactory.getMediaType(fileName).orElse(MediaType.APPLICATION_OCTET_STREAM).toString(); minioClient.uploadObject( UploadObjectArgs.builder().bucket(bucket) .object(name). filename(fileName).contentType(content).build() ); log.info("MinIO文件[{}]存储成功", name); return name; } catch (Exception e) { throw new CustomException("MinIO文件存储失败," + e.getMessage()); } } /** * 获取文件对象 * <p> * 返回结果是 GetObjectResponse类,它继承了 InputStream类 * GetObjectResponse使用后返回必须关闭以释放网络资源 * 此操作需要对此Object具有读权限 * * @param fileName 文件名 * @return 字节数组 */ public byte[] getObject(String fileName) { try (InputStream stream = minioClient.getObject( GetObjectArgs.builder() .bucket(bucket) .object(fileName) .build())) { return IOUtils.toByteArray(stream); } catch (Exception e) { throw new CustomException("MinIO文件获取失败"); } } /** * 文件下载 * <p> * 将对象的数据下载到磁盘 * * @param objectName 对象名称 * @param fileName 存储的磁盘文件路径 */ public void downloadObject(String objectName, String fileName) { try { minioClient.downloadObject( DownloadObjectArgs.builder() .bucket(bucket) .object(objectName) .filename(fileName) .build()); log.info("MinIO文件[{} --> {}]下载成功", objectName, fileName); } catch (Exception e) { throw new CustomException("MinIO文件下载失败"); } } /** * 获取 HTTP 方法、到期时间和自定义请求参数的对象的预签名 URL * * @param method 请求方式 * @param objectName 对象名称 * @param duration 期限 * @param unit 时间单位 * @param contentType 内容类型 * @return 预签名URL */ public String createPresignedObjectUrl(Method method, String objectName, int duration, TimeUnit unit, String contentType) { Map<String, String> reqParams = new HashMap<>(1); reqParams.put("response-content-type", contentType); try { return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder() .method(method) .bucket(bucket) .object(objectName) .expiry(duration, unit) .extraQueryParams(reqParams) .build()); } catch (Exception e) { throw new CustomException("Presigned URL生成失败," + e.getMessage()); } } /** * 生成一个给HTTP GET请求用的presigned URL。 * <p> * 浏览器/移动端的客户端可以用这个URL进行下载,即使其所在的存储桶是私有的。 * 这个presigned URL可以设置一个失效时间,默认值是7天。 * 使用此方法,可以提供给不用登录进行图片浏览,第三方共享访问等。 * * @param objectName 对象名称 * @param contentType 内容类型 * @return 预签名URL */ public String presignedGetObject(String objectName, String contentType) { return defaultExpiryParams(Method.GET, objectName, contentType); } /** * 默认设置过期时间为7天 * * @param method 请求方式 * @param objectName 对象名称 * @param contentType 内容类型 * @return 预签名URL */ private String defaultExpiryParams(Method method, String objectName, String contentType) { return createPresignedObjectUrl(method, objectName, 7, TimeUnit.DAYS, contentType); } /** * 生成一个给HTTP GET请求用的presigned URL。 * <p> * 浏览器/移动端的客户端可以用这个URL进行下载 * 使用此方法,可以提供给不用登录进行图片浏览,第三方共享访问等。 * * @param objectName 对象名称 * @param duration 期限 * @param unit 时间单位 * @param contentType 内容类型 * @return 预签名URL */ public String presignedGetObject(String objectName, int duration, TimeUnit unit, String contentType) { return createPresignedObjectUrl(Method.GET, objectName, duration, unit, contentType); } /** * 生成一个给HTTP PUT请求用的presigned URL。 * <p> * 浏览器/移动端的客户端可以用这个URL进行上传,即使其所在的存储桶是私有的 * 这个presigned URL可以设置一个失效时间,默认值是7天 * * @param objectName 对象名称 * @param contentType 内容类型 * @return 预签名URL */ public String presignedPutObject(String objectName, String contentType) { return defaultExpiryParams(Method.PUT, objectName, contentType); } /** * 生成一个给HTTP PUT请求用的presigned URL。 * <p> * 浏览器/移动端的客户端可以用这个URL进行上传,即使其所在的存储桶是私有的 * * @param objectName 对象名称 * @param duration 期限 * @param unit 时间单位 * @param contentType 内容类型 * @return 预签名URL */ public String presignedPutObject(String objectName, int duration, TimeUnit unit, String contentType) { return createPresignedObjectUrl(Method.PUT, objectName, duration, unit, contentType); } /** * 删除文件对象 * * @param objectName 对象名称 */ public void removeObject(String objectName) { try { minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucket).object(objectName).build()); log.info("MinIO文件[{}]删除成功", objectName); } catch (Exception e) { throw new CustomException("MinIO文件[" + objectName + "]删除失败," + e.getMessage()); } } /** * 批量删除文件对象 * * @param objects 文件集合 */ public void removeObjects(List<DeleteObject> objects) { try { Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucket).objects(objects).build()); for (Result<DeleteError> result : results) { // 删除文件不存在时,不会报错 DeleteError error = result.get(); log.info("MinIO文件[{}]删除失败,{}", error.objectName(), error.message()); } log.info("MinIO文件批量删除成功"); } catch (Exception e) { throw new CustomException("MinIO文件批量删除失败," + e.getMessage()); } } /** * 查询桶下对象 * * @param recursive 是否递归查找,如果是false,就模拟文件夹结构查找。 * @return {@link Item} */ public List<Item> listObjects(boolean recursive) { List<Item> list = new ArrayList<>(); try { Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucket).recursive(recursive).build()); for (Result<Item> result : results) { list.add(result.get()); } } catch (Exception e) { throw new CustomException("MinIO文件查询失败," + e.getMessage()); } return list; } /** * 条件查询 * * @param prefix 前缀 * @param recursive 是否递归 * @return {@link Item} */ public List<Item> listConditionsObjects(String prefix, boolean recursive) { List<Item> list = new ArrayList<>(); try { Iterable<Result<Item>> listObjects = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucket) .prefix(prefix).recursive(recursive).build()); for (Result<Item> result : listObjects) { list.add(result.get()); } } catch (Exception e) { throw new CustomException("MinIO文件条件查询失败," + e.getMessage()); } return list; } }
-
测试类
import com.jiyang.common.utils.MinioUtils; import com.jiyang.common.utils.file.FileUtils; import io.minio.messages.DeleteObject; import io.minio.messages.Item; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; import javax.annotation.Resource; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * MinIO 测试类 * 服务端部署方案参照文档 https://blog.csdn.net/jiahao1186/article/details/131024019 * * @author wujie * @version 1.0 */ @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class MinioTest { @Resource private MinioUtils minioUtils; // 对象名称 static String objectName = "2022/08/18/111111.jpg"; @Test public void presignedPutObject() { final String url = minioUtils.presignedPutObject(objectName, MediaType.IMAGE_JPEG_VALUE); System.out.println(url); } @Test public void presignedGetObject() { final String url = minioUtils.presignedGetObject("3b87e950352ac65c5eb643ddf9f2b21192138ae8.png", MediaType.IMAGE_PNG_VALUE); System.out.println(url); } @Test public void putObject() { final String s = minioUtils.uploadObject("测试1.xls", "D:\\software\\feiq\\Recv Files\\测试1.xls"); System.out.println(s); } @Test public void getObject() throws IOException { final byte[] file = minioUtils.getObject("2022/08/18/111111.jpg"); FileUtils.createFileByBytes("D:\\software\\feiq\\Recv Files\\00002.jpg", file); } @Test public void downloadObject() { minioUtils.downloadObject(objectName, "D:\\software\\feiq\\Recv Files\\000021.jpg"); } @Test public void removeObject() { minioUtils.removeObject("2022/08/19/111111.jpg"); } @Test public void removeObjects() { List<DeleteObject> objects = new ArrayList<>(); objects.add(new DeleteObject("2022/08/19/111111.jpg")); objects.add(new DeleteObject("2022/08/19/2.svg")); minioUtils.removeObjects(objects); } @Test public void listObjects() { final List<Item> list = minioUtils.listObjects(true); for (final Item item : list) { System.out.println(item.objectName() + "\t" + item.size()); } } @Test public void listConditionsObjects() { final List<Item> list = minioUtils.listConditionsObjects("", false); for (final Item item : list) { System.out.println(item.objectName() + "\t" + item.size()); //2022/08/18/111111.jpg 864090 //2022/08/19/049$ZY·DJ$43166$0003.jpg 844713 } } }
评论