高性能对象存储MinIO学习API使用使用api创建文件夹MinIO工具类

MinIO 是GlusterFS创始人之一Anand Babu Periasamy发布的开源项目,基于Apache V2 license 100% 开放源代码。MinIO采用Golang实现,客户端支持Java、Python、Javacript、Golang语言等。

其设计的主要目标是作为私有云对象存储的标准方案。非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据、容器和虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T。

MinIO有中文文档,见:http://docs.minio.org.cn/

1、MinIO的优点

(1)安装部署(运维简单)

MinIO在安装过程是黑盒的,不用深入关注它的架构,也不需要进行零件组装,基本上可以做到开箱即用。普通的技术人员就能够参与后期的运维。

MinIO提供了两种部署方式:单机部署和分布式,两种部署方式都非常简单,其中分布式部署还提供了纠删码功能来降低数据丢失的风险。

(2)UI界面

MinIO自带UI界面,且页面不需要你单独的部署,和服务端一并安装。开箱即用,爱了爱了。

(3)高性能

MinIO号称是世界上速度最快的对象存储服务器。在标准硬件上,对象存储的读/写速度最高可以达到183 GB/s和171 GB/s。对象存储可以充当主存储层,以处理Spark、Presto、TensorFlow、H2O.ai等各种复杂工作负载以及成为Hadoop HDFS的替代品。MinIO用作云原生应用程序的主要存储,与传统对象存储相比,云原生应用程序需要更高的吞吐量和更低的延迟。而这些都是MinIO能够达成的性能指标。

(4)容器化支持

MinIO 符合一切原生云计算的架构和构建过程,并且包含最新的云计算的全新的技术和概念。其中包括支持Kubernetes 、Docker、微服和多租户的的容器技术。

(5)丰富的SDK支持

MinIO几乎提供了所有主流开发语言的SDK以及文档。

(6)AWS S3标准兼容

亚马逊云的 S3 API(接口协议) 是在全球范围内达到共识的对象存储的协议,是全世界内大家都认可的标准。MinIO 在很早的时候就采用了 S3 兼容协议,并且MinIO 是第一个支持 S3 Select 的产品. MinIO对其兼容性的全面性感到自豪, 并且得到了 750多个组织的认同, 包括Microsoft Azure使用MinIO的S3网关 - 这一指标超过其他同类产品的总和。

怎么理解呢?可以这么说你目前为了节约成本使用MinIO,等你的公司壮大了、有钱了。不想自己运维基础设施了,你就可以把对象存储放到云上,只要云厂商支持S3标准,你的应用程序是不需要重新开发的。

(7)可扩展性

MinIO利用了Web缩放器的来之不易的知识,为对象存储带来了简单的缩放模型。这是我们坚定的理念 “简单可扩展.” 在 MinIO, 扩展从单个群集开始,该群集可以与其他MinIO群集联合以创建全局名称空间, 并在需要时可以跨越多个不同的数据中心。通过添加更多集群可以扩展名称空间, 更多机架,直到实现目标。

2、MinIO的缺点

MinIO不支持动态增加节点,MinIO创始人的设计理念就是动态增加节点太复杂,后续会采用其它方案来支持扩容。目前只能是新增节点后手动重启系统才生效,系统会自动平衡数据,这种设计到底对系统后续有什么影响,我觉得使用者需要考虑清楚点。

这里有个方案可以参考下,就是事先准备好一套容量适中且是开启状态的MinIO集群,当业务量陡增、原MinIO集群容量告警时,应用自动启用备份MinIO集群,后续再整合两个集群的文件成一个更大的集群。否则不支持在线动态扩容,确实是个硬伤。

Java之MinIO存储桶和对象API使用

MinIO Java Client SDK提供简单的API来访问任何与Amazon S3兼容的对象存储服务。

  • 官方demo: https://github.com/minio/minio-java
  • 官方文档:https://docs.min.io/docs/java-client-api-reference.html

新版 MinIO和旧版在API使用上还是有一定的区别,比如:新版 MinIO采用 Builder构建者模式来构造 MinioClient对象。所以,官方demo仅供参考,尽量查看英文官方文档。
在这里插入图片描述

一、环境搭建

1、创建一个 maven项目,引入依赖:

    <!-- minio依赖--><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.3.3</version></dependency><!-- 官方 miniodemo需要的依赖--><dependency><groupId>me.tongfei</groupId><artifactId>progressbar</artifactId><version>0.7.4</version></dependency><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>4.9.2</version></dependency>

这里我把 官方demo 放到了我的项目中,所以必须引入 progressbar依赖。否则忽略它。

2、初始化Minio客户端
注意:

  • 新版 MinIO 采用 Builder构建者模式来构造 MinioClient对象。

  • API端口和 访问控制台端口不要搞混了。

        // 初始化Minio客户端MinioClient minioClient = MinioClient.builder().endpoint("http://xxx.xxx.xxx.xxx:9000/").credentials("admin", "xxx").build();System.out.println(minioClient);
    

获取到 MinioClient对象,就可以进行 MinIO的 API操作使用了。

二、存储桶基本使用

1、检查存储桶是否存在。

  • boolean bucketExists(BucketExistsArgs args):检查存储桶是否存在。

示例:

BucketExistsArgs btest2 = BucketExistsArgs.builder().bucket("btest2").build();
boolean existFlag = minioClient.bucketExists(btest2);

注意: 旧版参数是字符串 - 存储桶名,新版需要 build来构造 BucketExistsArgs对象。

2、创建存储桶

  • public void makeBucket(MakeBucketArgs args):创建一个启用给定区域和对象锁定功能的存储桶。

示例1:存储桶不存在则创建

		String bucketName = "java.minio.demo";// 存储桶不存在则创建if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());System.out.printf("%s,创建成功
", bucketName);}else{System.out.printf("%s,已存在
", bucketName);}

3、查询存储桶

3.1 查询所有桶的列表信息

  • public List listBuckets():列出所有桶的桶信息。

示例:

        List<Bucket> bucketList = minioClient.listBuckets();bucketList.forEach(bucket -> {System.out.printf("存储桶名:%s,创建时间:%s 
", bucket.name(), bucket.creationDate());});

在这里插入图片描述
注意:桶的创建时间默认是美国时间,创建桶时我们可以指定桶的时区或者设置 MinIO服务器时区。

4、删除存储桶

  • public void removeBucket(RemoveBucketArgs args):删除一个空桶。

示例:

String bucketName2 = "btest3";       minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName2).build());

注意:如果存储桶存在对象不为空时,删除会报错。

二、对象基本使用

1、上传对象

1.1 PutObject方法

  • public ObjectWriteResponse putObject(PutObjectArgs args):将给定的流上传为存储桶中的对象。

示例1,InputStream上传:

        String bucketName = "java.minio.demo";// 创建InputStream上传File file = new File("D:\DownUpLoadTempFiles\100元.jpg");InputStream inputStream = new FileInputStream(file);long start = System.currentTimeMillis();// 上传流minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object("2021/11/28/" + file.getName()).stream(inputStream, inputStream.available(), -1).build());inputStream.close();System.out.println("uploaded successfully 耗时:" + (System.currentTimeMillis() - start));

在这里插入图片描述
注意:

  • 添加的Object大小不能超过 5GB。
  • 默认情况下,如果已存在同名Object且对该Object有访问权限,则新添加的Object将覆盖原有的Object,并返回 200 OK。
  • OSS没有文件夹的概念,所有资源都是以文件来存储,但您可以通过创建一个以正斜线(/)结尾,大小为 0的Object来创建模拟文件夹(指定 /后,默认会自动创建)。

上传文件是也可以使用SSE-C加密,添加自定义元数据及消息头等操作。

1.2 uploadObject方法

  • public void uploadObject(UploadObjectArgs args):将文件中的内容作为存储桶中的对象上传。

不太常用,一般适合上传磁盘文件(mc cp命令更方便)。
示例:

        String bucketName = "java.minio.demo";long start = System.currentTimeMillis();// 上传文件minioClient.uploadObject(UploadObjectArgs.builder().bucket(bucketName).object("2021/11/28/100元2.jpg").filename("D:\DownUpLoadTempFiles\100元.jpg").build());System.out.println("uploaded successfully 耗时:" + (System.currentTimeMillis() - start));

2、获取对象

2.1 getObject方法

  • public GetObjectResponse getObject(GetObjectArgs args):获取对象的数据。

示例:

		String bucketName = "java.minio.demo";// GetObjectResponse 继承了 InputStream类GetObjectResponse objectResponse = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object("2021/11/28/100元2.jpg").build());System.out.println(objectResponse.bucket());System.out.println(objectResponse.object());byte[] allBytes = objectResponse.readAllBytes();System.out.println(allBytes);// Close the input stream.objectResponse.close();

注意:

  • 返回结果是 GetObjectResponse类,它继承了 InputStream类
  • GetObjectResponse使用后返回必须关闭以释放网络资源。
  • 此操作需要对此Object具有读权限。

2.2 downloadObject方法

  • public void downloadObject(DownloadObjectArgs args):将对象的数据下载到磁盘。

        String bucketName = "java.minio.demo";minioClient.downloadObject(DownloadObjectArgs.builder().bucket(bucketName).object("2021/11/28/100元2.jpg").filename("D:\TempFiles\100元2.jpg") // 必须指定文件名.build());
    

2.3 getPresignedObjectUrl方法

  • public String getPresignedObjectUrl(GetPresignedObjectUrlArgs args):获取 HTTP 方法、到期时间和自定义请求参数的对象的预签名 URL。

示例:返回获取文件对象的URL GET请求,此 URL过期时间为一分钟。

        String bucketName = "java.minio.demo";String objectUrl = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucketName).object("2021/11/28/100元2.jpg").method(Method.GET)//.expiry(60) // 单位秒.expiry(30, TimeUnit.SECONDS).build());System.out.println(objectUrl);

返回带签名的URL有点小长,过期之后访问会报错。
在这里插入图片描述
使用此方法,可以提供给不用登录进行图片浏览,第三方共享访问等。
我们还可以对返回 URL,根据业务做一些参数验签等控制。

2.4 getPresignedPostFormData方法

  • public Map<String,String> getPresignedPostFormData(PostPolicy policy):获取对象的PostPolicy的表单数据以使用 POST 方法上传其数据。

       String bucketName = "java.minio.demo";String objectName = "2021/11/28/100元3.jpg";// 1. 创建一个Post 策略// 为存储桶创建一个上传策略,过期时间为7天PostPolicy policy = new PostPolicy(bucketName, ZonedDateTime.now().plusDays(7));// 设置一个参数key-value,值为上传对象的名称(保存在桶中的名字)policy.addEqualsCondition("key", objectName);// 添加 Content-Type以"image/"开头,表示只能上传照片policy.addStartsWithCondition("Content-Type", "image/");// 设置上传文件的大小 10kiB to 10MiB.policy.addContentLengthRangeCondition(10 * 1024, 10 * 1024 * 1024);// 2. 获取策略的 认证令牌、签名等信息Map<String, String> formData = minioClient.getPresignedPostFormData(policy);// 3.模拟第三方,使用 OkHttp调用 Post上传对象// 创建 MultipartBody对象MultipartBody.Builder multipartBuilder = new MultipartBody.Builder();multipartBuilder.setType(MultipartBody.FORM);for (Map.Entry<String, String> entry : formData.entrySet()) {multipartBuilder.addFormDataPart(entry.getKey(), entry.getValue());}multipartBuilder.addFormDataPart("key", objectName);// 必须要和策略参数一样multipartBuilder.addFormDataPart("Content-Type", "image/png");File uploadFile = new File("D:\DownUpLoadTempFiles\100元.jpg");// 上传文件的 fileName自定义,这里方便就用 objectNamemultipartBuilder.addFormDataPart("file", objectName, RequestBody.create(uploadFile, null));// 使用OkHttp调用Post上传对象Request request =new Request.Builder().url("http://192.168.198.110:9000/" + bucketName).post(multipartBuilder.build()).build();OkHttpClient httpClient = new OkHttpClient().newBuilder().build();Response response = httpClient.newCall(request).execute();if (response.isSuccessful()) {System.out.println("uploaded successfully using POST object");} else {System.out.println("Failed to upload Pictures");}
    

使用此方法,获取对象的上传策略(包含签名、文件信息、路径等),然后使用这些信息采用 POST 方法的表单数据上传数据。也就是可以生成一个临时上传的信息对象,第三方可以使用这些信息,就可以上传文件。

注意:

  • 第三方请求中的签名必须和 创建策略中的签名参数等一致,不符合策略要求的就会上传失败。

一般使用场景:

  1. 第三方请求应用服务器接口,来获取一个上传策略信息。
  2. 第三方使用 Http+访问策略信息直接请求应用服务器接口进行上传文件。

3、复制对象

3.1 copyObject方法

  • public ObjectWriteResponse copyObject(CopyObjectArgs args):通过服务器端从另一个对象复制数据来创建一个对象。

示例:

        String bucketName = "java.minio.demo";String bucketName2 = "btest2";// 将 bucketName中的100元2.jpg文件,复制到bucketName2桶下minioClient.copyObject(CopyObjectArgs.builder().source(CopySource.builder().bucket(bucketName).object("2021/11/28/100元2.jpg").build()).bucket(bucketName2).object("2021/11/28/100元copy.jpg").build());

mc cp命令更方便。

4、删除对象

4.1 removeObject方法

  • public void removeObject(RemoveObjectArgs args) :移除一个对象。

示例:

       String bucketName = "java.minio.demo";minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object("2021/11/28/100元2.jpg")//.versionId("my-versionid") //还可以删除指定版本号的对象.build());

4.2 removeObjects方法

  • public Iterable removeObjects(RemoveObjectsArgs args):懒惰地删除多个对象。它需要迭代返回的 Iterable 以执行删除。

示例:

        String bucketName = "java.minio.demo";// 构建需要删除的对象List<DeleteObject> objects = new LinkedList<>();objects.add(new DeleteObject("2021/11/28/100元.jpg"));objects.add(new DeleteObject("2021/11/28/100元2.jpg"));objects.add(new DeleteObject("2021/11/28/100元3.jpg"));// 删除Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(objects).build());for (Result<DeleteError> result : results) {// 删除文件不存在时,不会报错DeleteError error = result.get();System.out.println("Error in deleting object " + error.objectName() + "; " + error.message());}

5、桶的对象信息查询

  • public Iterable listObjects(ListObjectsArgs args):列出桶的对象信息。

5.1 查询桶下对象

        String bucketName2 = "btest2";Iterable<Result<Item>> listObjects = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName2).build());for (Result<Item> result : listObjects) {Item item = result.get();System.out.println(item.objectName() + "	" + item.size() );}

在这里插入图片描述

5.2 递归查询桶下对象

        String bucketName2 = "btest2";Iterable<Result<Item>> listObjects = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName2).recursive(true).build());for (Result<Item> result : listObjects) {Item item = result.get();System.out.println(item.objectName() + "	" + item.size() );}

在这里插入图片描述

5.3 条件查询

        // 条件查询Iterable<Result<Item>> listObjects = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName2).startAfter("2021").prefix("2") // 指定前缀.maxKeys(100) // 最大数量.recursive(true) // 递归.build());for (Result<Item> result : listObjects) {Item item = result.get();System.out.println(item.objectName() + "	" + item.size() );}

在这里插入图片描述

MinIO api创建文件夹

近期公司业务需求,将OSS迁移到MinIO上,发现创建空文件夹有很大的问题,请注意,**一定要引*7.1.0*版本的minio SDK,才能实现创建空文件夹。

代码

minioClient.putObject(PutObjectArgs.builder().bucket("my-bucketname").object("path/to/").stream(new ByteArrayInputStream(new byte[] {}), 0, -1).build());

实际创建的还是文件对象,只不过以"/"结尾,模拟了文件夹或目录

其他版本,同样以"/"结尾

3.0.10:创建文件夹报错

7.0.2:传入"/“结尾ObjectName,创建出来的是文件,自动去掉了”/"

MinIO工具类

pom.xml中引依赖

<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>7.1.0</version>
</dependency>

配置

minio:endpoint: http://127.0.0.1:9000 #MinIO服务所在地址bucketName: test #存储桶名称accessKey: admin #访问的keysecretKey: admin123 #访问的秘钥

项目启动,创建bean:MinioConfig.java

import com.mgmiot.dlp.file.utils.MinioUtils;
import lombok.extern.slf4j.Slf4j;
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;
import org.springframework.integration.annotation.IntegrationComponentScan;@Configuration
@IntegrationComponentScan
@Slf4j
public class MinioConfig {@Value("${minio.endpoint}")private String endpoint;@Value("${minio.bucketName}")private String bucketName;@Value("${minio.accessKey}")private String accessKey;@Value("${minio.secretKey}")private String secretKey;@Beanpublic MinioUtils creatMinioClient() {return new MinioUtils(endpoint, bucketName, accessKey, secretKey);}}

Minio工具类:MinioUtils.java

import com.alibaba.fastjson.JSONObject;
import io.minio.BucketExistsArgs;
import io.minio.CopyObjectArgs;
import io.minio.CopySource;
import io.minio.GetBucketPolicyArgs;
import io.minio.GetObjectArgs;
import io.minio.ListObjectsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.ObjectStat;
import io.minio.ObjectWriteResponse;
import io.minio.PostPolicy;
import io.minio.PutObjectArgs;
import io.minio.RemoveBucketArgs;
import io.minio.RemoveObjectArgs;
import io.minio.Result;
import io.minio.StatObjectArgs;
import io.minio.UploadObjectArgs;
import io.minio.errors.BucketPolicyTooLargeException;
import io.minio.errors.ErrorResponseException;
import io.minio.errors.InsufficientDataException;
import io.minio.errors.InternalException;
import io.minio.errors.InvalidBucketNameException;
import io.minio.errors.InvalidExpiresRangeException;
import io.minio.errors.InvalidResponseException;
import io.minio.errors.RegionConflictException;
import io.minio.errors.ServerException;
import io.minio.errors.XmlParserException;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;/*** @Author: zrs* @Date: 2020/12/01/10:02* @Description: Minio工具类*/
@Slf4j
public class MinioUtils {private static MinioClient minioClient;private static String endpoint;private static String bucketName;private static String accessKey;private static String secretKey;private static final String SEPARATOR = "/";private MinioUtils() {}public MinioUtils(String endpoint, String bucketName, String accessKey, String secretKey) {MinioUtils.endpoint = endpoint;MinioUtils.bucketName = bucketName;MinioUtils.accessKey = accessKey;MinioUtils.secretKey = secretKey;createMinioClient();}/*** 创建minioClient*/public void createMinioClient() {try {if (null == minioClient) {log.info("minioClient create start");minioClient = MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();createBucket();log.info("minioClient create end");}} catch (Exception e) {log.error("连接MinIO服务器异常:{}", e);}}/*** 获取上传文件的基础路径** @return url*/public static String getBasisUrl() {return endpoint + SEPARATOR + bucketName + SEPARATOR;}操作存储桶 /*** 初始化Bucket** @throws Exception 异常*/private static void createBucket()throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException, RegionConflictException {if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());}}/*** 验证bucketName是否存在** @return boolean true:存在*/public static boolean bucketExists()throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());}/*** 创建bucket** @param bucketName bucket名称*/public static void createBucket(String bucketName)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException, RegionConflictException {if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());}}/*** 获取存储桶策略** @param bucketName 存储桶名称* @return json*/private JSONObject getBucketPolicy(String bucketName)throws IOException, InvalidKeyException, InvalidResponseException, BucketPolicyTooLargeException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, InsufficientDataException, ErrorResponseException {String bucketPolicy = minioClient.getBucketPolicy(GetBucketPolicyArgs.builder().bucket(bucketName).build());return JSONObject.parseObject(bucketPolicy);}/*** 获取全部bucket* <p>* https://docs.minio.io/cn/java-client-api-reference.html#listBuckets*/public static List<Bucket> getAllBuckets()throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.listBuckets();}/*** 根据bucketName获取信息** @param bucketName bucket名称*/public static Optional<Bucket> getBucket(String bucketName)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();}/*** 根据bucketName删除信息** @param bucketName bucket名称*/public static void removeBucket(String bucketName)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());}操作文件对象 /*** 判断文件是否存在** @param bucketName 存储桶* @param objectName 对象* @return true:存在*/public static boolean doesObjectExist(String bucketName, String objectName) {boolean exist = true;try {minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());} catch (Exception e) {exist = false;}return exist;}/*** 判断文件夹是否存在** @param bucketName 存储桶* @param objectName 文件夹名称(去掉/)* @return true:存在*/public static boolean doesFolderExist(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) {exist = false;}return exist;}/*** 根据文件前置查询文件** @param bucketName bucket名称* @param prefix 前缀* @param recursive 是否递归查询* @return MinioItem 列表*/public static List<Item> getAllObjectsByPrefix(String bucketName, String prefix,boolean recursive)throws ErrorResponseException, InsufficientDataException, InternalException, InvalidBucketNameException, InvalidKeyException, InvalidResponseException,IOException, NoSuchAlgorithmException, ServerException, XmlParserException {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 bucket名称* @param objectName 文件名称* @return 二进制流*/public static InputStream getObject(String bucketName, String objectName)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());}/*** 断点下载** @param bucketName bucket名称* @param objectName 文件名称* @param offset 起始字节的位置* @param length 要读取的长度* @return 流*/public InputStream getObject(String bucketName, String objectName, long offset, long length)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).offset(offset).length(length).build());}/*** 获取路径下文件列表** @param bucketName bucket名称* @param prefix 文件名称* @param recursive 是否递归查找,如果是false,就模拟文件夹结构查找* @return 二进制流*/public static 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 对象名*/public static ObjectWriteResponse putObject(String bucketName, MultipartFile file,String objectName, String contentType)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {InputStream inputStream = file.getInputStream();return minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).contentType(contentType).stream(inputStream, inputStream.available(), -1).build());}/*** 上传本地文件** @param bucketName 存储桶* @param objectName 对象名称* @param fileName 本地文件路径*/public static ObjectWriteResponse putObject(String bucketName, String objectName,String fileName)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.uploadObject(UploadObjectArgs.builder().bucket(bucketName).object(objectName).filename(fileName).build());}/*** 通过流上传文件** @param bucketName 存储桶* @param objectName 文件对象* @param inputStream 文件流*/public static ObjectWriteResponse putObject(String bucketName, String objectName,InputStream inputStream)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(inputStream, inputStream.available(), -1).build());}/*** 创建文件夹或目录** @param bucketName 存储桶* @param objectName 目录路径*/public static ObjectWriteResponse putDirObject(String bucketName, String objectName)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(new ByteArrayInputStream(new byte[]{}), 0, -1).build());}/*** 获取文件信息, 如果抛出异常则说明文件不存在** @param bucketName bucket名称* @param objectName 文件名称*/public static ObjectStat statObject(String bucketName, String objectName)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());}/*** 拷贝文件** @param bucketName bucket名称* @param objectName 文件名称* @param srcBucketName 目标bucket名称* @param srcObjectName 目标文件名称*/public static ObjectWriteResponse copyObject(String bucketName, String objectName,String srcBucketName, String srcObjectName)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.copyObject(CopyObjectArgs.builder().source(CopySource.builder().bucket(bucketName).object(objectName).build()).bucket(srcBucketName).object(srcObjectName).build());}/*** 删除文件** @param bucketName bucket名称* @param objectName 文件名称*/public static void removeObject(String bucketName, String objectName)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());}/*** 批量删除文件** @param bucketName bucket* @param keys 需要删除的文件列表* @return*//*public static Iterable<Result<DeleteError>> removeObjects(String bucketName, List<String> keys) {List<DeleteObject> objects = new LinkedList<>();keys.forEach(s -> {objects.add(new DeleteObject(s));});return minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(objects).build());}*/public static void removeObjects(String bucketName, List<String> keys) {List<DeleteObject> objects = new LinkedList<>();keys.forEach(s -> {objects.add(new DeleteObject(s));try {removeObject(bucketName, s);} catch (Exception e) {log.error("批量删除失败!error:{}",e);}});}操作Presigned /*** 获取文件外链** @param bucketName bucket名称* @param objectName 文件名称* @param expires 过期时间 <=7 秒级* @return url*/public static String getPresignedObjectUrl(String bucketName, String objectName,Integer expires)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, InvalidExpiresRangeException, ServerException, InternalException, NoSuchAlgorithmException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.presignedGetObject(bucketName, objectName, expires);}/*** 给presigned URL设置策略** @param bucketName 存储桶* @param objectName 对象名* @param expires 过期策略* @return map*/public static Map<String, String> presignedGetObject(String bucketName, String objectName,Integer expires)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, InvalidExpiresRangeException, ServerException, InternalException, NoSuchAlgorithmException, XmlParserException, InvalidBucketNameException, ErrorResponseException {PostPolicy policy = new PostPolicy(bucketName, objectName,ZonedDateTime.now().plusDays(7));policy.setContentType("image/png");return minioClient.presignedPostPolicy(policy);}/*** 将URLDecoder编码转成UTF8** @param str* @return* @throws UnsupportedEncodingException*/public static String getUtf8ByURLDecoder(String str) throws UnsupportedEncodingException {String url = str.replaceAll("%(?![0-9a-fA-F]{2})", "%25");return URLDecoder.decode(url, "UTF-8");}}
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;import java.util.ArrayList;
import java.util.List;@Service
@RequiredArgsConstructor
public class AttachmentManagementService {private final MinIOFileManagementService fileManagementService;public String save(ModuleTypeEnum moduleTypeEnum, Long sourceId, MultipartFile attachment) {return save(moduleTypeEnum, sourceId.toString(), attachment);}public String save(ModuleTypeEnum moduleTypeEnum, String sourceId, MultipartFile attachment) {if (attachment != null) {return fileManagementService.storage(moduleTypeEnum, sourceId, attachment);}return null;}public List<String> save(ModuleTypeEnum moduleTypeEnum, Long sourceId, MultipartFile[] attachments) {List<String> result = new ArrayList<>();if (attachments != null) {for (MultipartFile attachment : attachments) {String fileName = save(moduleTypeEnum, sourceId, attachment);if (fileName != null) {result.add(fileName);}}}return result;}public void delete(ModuleTypeEnum moduleTypeEnum, Long sourceId) {fileManagementService.delete(moduleTypeEnum, sourceId.toString());}public void delete(ModuleTypeEnum moduleTypeEnum, List<Long> sourceIdList) {if (DataStructureUtils.isValid(sourceIdList)) {for (Long sourceId : sourceIdList) {delete(moduleTypeEnum, sourceId);}}}public String update(ModuleTypeEnum moduleTypeEnum, Long sourceId, MultipartFile attachment) {delete(moduleTypeEnum, sourceId);return save(moduleTypeEnum, sourceId, attachment);}public List<String> update(ModuleTypeEnum moduleTypeEnum, Long sourceId, MultipartFile[] attachments, List<String> delAttachmentNameList) {if (DataStructureUtils.isValid(delAttachmentNameList)) {for (String delAttachmentName : delAttachmentNameList) {fileManagementService.delete(delAttachmentName);}}return save(moduleTypeEnum, sourceId, attachments);}public void delete(List<String> delAttachmentNameList) {if (DataStructureUtils.isValid(delAttachmentNameList)) {for (String delAttachmentName : delAttachmentNameList) {fileManagementService.delete(delAttachmentName);}}}}
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Item;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;import javax.annotation.PostConstruct;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;@Log4j
@Service
public class MinIOFileManagementService {@Value("${url}")private String url;@Value("${accessKey}")private String accessKey;@Value("${secretKey}")private String secretKey;@Value("${bucket}")private String bucket;private MinioClient minioClient;@PostConstructprivate void init() {try {minioClient = MinioClient.builder().endpoint(url).credentials(accessKey, secretKey).build();if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build())) {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());}} catch (Exception ignore) {log.warn("Initialization MinIO client failed.");}}public List<String> listFile(String directory) {List<String> result = new ArrayList<>();try {for (Result<Item> next : minioClient.listObjects(ListObjectsArgs.builder().bucket(bucket).prefix(directory).build())) {Item item = next.get();result.add(item.objectName());}} catch (Exception e) {log.warn(e);}return result;}public List<String> listName(String prefix) {List<String> result = new ArrayList<>();try {for (Result<Item> next : minioClient.listObjects(ListObjectsArgs.builder().bucket(bucket).prefix(prefix).build())) {Item item = next.get();if (!item.isDir()) {result.add(item.objectName());}}} catch (Exception e) {log.warn(e);}return result;}public boolean isExists(String name) {return listName(name).contains(name);}public String getAccessUrl(String name) {if (isExists(name)) {try {return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucket).method(Method.GET).object(name).build());} catch (Exception e) {log.warn(e);}}return null;}public String getContent(String name) {if (isExists(name)) {try (BufferedReader reader = new BufferedReader(new InputStreamReader(minioClient.getObject(GetObjectArgs.builder().bucket(bucket).object(name).build())))) {return reader.lines().collect(Collectors.joining("\n"));} catch (Exception e) {log.warn(e);}}return null;}public void download(OutputStream outputStream, String name) throws Exception {try (GetObjectResponse object = minioClient.getObject(GetObjectArgs.builder().bucket(bucket).object(name).build())) {int len;byte[] buffer = new byte[1024];while ((len = object.read(buffer)) > 0) {outputStream.write(buffer, 0, len);}}}public void createDirectory(String directory) {try {minioClient.putObject(PutObjectArgs.builder().bucket(bucket).object(directory).stream(new ByteArrayInputStream(new byte[]{}), 0, -1).build());} catch (Exception e) {log.warn(e);}}public String storage(String name, MultipartFile file) {try {minioClient.putObject(PutObjectArgs.builder().bucket(bucket).object(name).contentType(file.getContentType()).stream(file.getInputStream(), file.getSize(), file.getSize() > -1 ? -1 : 5 * 1024 * 1024).build());} catch (Exception e) {log.warn(e);}return name;}@SneakyThrowspublic String storage(String name, String content) {byte[] bytes = content.getBytes();minioClient.putObject(PutObjectArgs.builder().bucket(bucket).object(name).contentType("text/plain").stream(new ByteArrayInputStream(bytes), bytes.length, -1).build());return name;}public void delete(String name) {List<String> objectNameList = listName(name);if (DataStructureUtils.isValid(objectNameList)) {try {for (String objectName : objectNameList) {minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucket).object(objectName).build());}} catch (Exception e) {log.warn(e);}}}private String getObjectName(Object... keys) {return Arrays.stream(keys).map(Object::toString).collect(Collectors.joining("/"));}public List<String> listName(ModuleTypeEnum moduleType, String directory) {return listName(getObjectName(moduleType.name(), directory, ""));}public String getName(ModuleTypeEnum moduleType, String directory) {List<String> nameList = listName(moduleType, directory);return DataStructureUtils.isValid(nameList) ? nameList.get(0) : null;}public boolean isExists(ModuleTypeEnum moduleType, String directory, String fileName) {return isExists(getObjectName(moduleType.name(), directory, fileName));}public String getAccessUrl(ModuleTypeEnum moduleType, String directory, String fileName) {return getAccessUrl(getObjectName(moduleType.name(), directory, fileName));}public String getContent(ModuleTypeEnum moduleType, String directory) {return getContent(getName(moduleType, directory));}public String getContent(ModuleTypeEnum moduleType, String directory, String fileName) {return getContent(getObjectName(moduleType.name(), directory, fileName));}public String storage(ModuleTypeEnum moduleType, String directory, MultipartFile file) {String originalFileName = file.getOriginalFilename();if (isExists(moduleType, directory, originalFileName)) {originalFileName = GeniusFileUtils.modifyDuplicateFileName(originalFileName);}return storage(getObjectName(moduleType.name(), directory, originalFileName), file);}public String storage(ModuleTypeEnum moduleType, String directory, String fileName, MultipartFile file) {return storage(getObjectName(moduleType.name(), directory, fileName), file);}public String storage(ModuleTypeEnum moduleType, String directory, String fileName, String content) {return storage(getObjectName(moduleType.name(), directory, fileName), content);}public void delete(ModuleTypeEnum moduleType, String directory) {delete(getObjectName(moduleType.name(), directory, ""));}public void delete(ModuleTypeEnum moduleType, String directory, String fileName) {delete(getObjectName(moduleType.name(), directory, fileName));}}

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

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

相关文章

rmi远程反序列化rce漏洞_Apache Dubbo Provider默认反序列化远程代

背景近日&#xff0c;Apache Dubbo披露了Provider默认反序列化远程代码执行漏洞(CVE-2020-1948)&#xff0c;攻击者可构造恶意请求&#xff0c;从而执行任意代码。具体信息如上图所示。在官方邮件中&#xff0c;漏洞报告者还提供了官方的PoC脚本&#xff0c;感兴趣的读者可以自…

操作痕迹包括那些_高级消防设施操作员专题之:走近气体灭火系统

按照《消防设施操作员职业技能标准》的规定&#xff0c;安装有气体灭火系统的单位&#xff0c;应当配置高级消防设施操作员。由于这些单位通常情况下都是消防安全重点单位、火灾高危单位&#xff0c;可以预见&#xff0c;高级消防设施操作员作为消防行业的高技能人才&#xff0…

flutter不支持热更新_Flutter 在安卓上可以实现热更新了

本文由 句号君 授权投稿原文链接&#xff1a;https://blog.csdn.net/qizewei123/article/details/102963340Flutter 官方在 GitHub 上声明是暂时不支持热更新的&#xff0c;但是在 Flutter 的源码里&#xff0c;是有一部分预埋的热更新相关的代码&#xff0c;并且通过一些我们自…

jar包在windows后台运行,通过.bat文件

jar包在windows后台运行.bat 一、IDEA打成jar包 这里不再赘述 二、在windows后台运行jar包 在cmd中可以使用java -jar xxxxx.jar方式运行一个jar文件&#xff0c;这种方法运行一旦关闭该cmd界面就会停止运行。编辑.bat文件&#xff0c;使用javaw方式运行不用担心文件会在不小…

java 枚举(enum) 全面解读

枚举类型是单例模式的。你需要实例化一次&#xff0c;然后再整个程序之中就可以调用他的方法和成员变量了。 枚举类型使用单例模式是因为他的值是固定的&#xff0c;不需要发生改变。 简介 枚举是Java1.5引入的新特性&#xff0c;通过关键字enum来定义枚举类。枚举类是一种特殊…

修改表名_面试官:如何批量修改mysql表字段、表、数据库字符集和排序规则

概述目前数据库字符集统一用的utf8&#xff0c;由于项目需要&#xff0c;引进了表情&#xff0c;但是utf8mb5才支持表情字符&#xff0c;所以需统一修改数据库字符集&#xff0c;下面介绍批量修改数据库字符集的办法。修正顺序是字段级别>表级别>库级别。一、批量修改整个…

Maven命令 install 和 package的区别

Maven命令 install 和 package的区别 Maven是目前十分流行的项目构建工具以及依赖解决工具&#xff0c;其提供的常用指令中有两个很容易引起使用者的疑惑&#xff0c; 那就是 install 和 package &#xff0c; 那么这两个命令到底有啥区别呢&#xff1f; Maven install 安装…

如何重启_消费市场按下重启键,企业该如何提前布局

2020广发卡携手企业和消费者&#xff0c;共同按下重启键&#xff0c;让我们放下包袱&#xff0c;轻松前行。当疫情结束后&#xff0c;你想做什么&#xff1f;也许是去见想见的人&#xff0c;和他一起去吃想吃的美食&#xff1b;也许是约上三五好友&#xff0c;或带着最亲的家人…

Linux中使用netstat命令的基本操作,排查端口号的占用情况

Linux中netstat命令详解 Netstat是控制台命令,是一个监控TCP/IP网络的非常有用的工具&#xff0c;它可以显示路由表、实际的网络连接以及每一个网络接口设备的状态信息。Netstat用于显示与IP、TCP、UDP和ICMP协议相关的统计数据&#xff0c;一般用于检验本机各端口的网络连接情…

与context的关系_Android-Context

一.简介Context 翻译为上下文环境&#xff0c;是一个应用程序环境信息的接口。如果以 Android 系统角度来看可以理解为某一与操作系统的交互的具体场景&#xff0c;比如 Activity 的具体功能&#xff0c;Service 的后台运行等。如果以程序的角度看&#xff0c;Context 是一个抽…

Linux中sudo、su和su -命令的区别

Linux中sudo、su和su -命令的区别小结 我们知道&#xff0c;在Linux下对很多文件进行修改都需要有root&#xff08;管理员&#xff09;权限&#xff0c;比如对/ect/profile等文件的修改。下面这篇文章主要给大家总结介绍了关于Linux中sudo、su和su -命令的区别的相关资料&…

如何根据进程号去查端口号?

1.查出进程号 eg: ps -ef |grep conet- 2.根据当前进程号获得端口号&#xff1a; eg: netstat -anp |grep 4118 得到当前的端口是8761

Linux中如何查看某个端口是否被占用的方法

LINUX中如何查看某个端口是否被占用的方法 之前查询端口是否被占用一直搞不明白&#xff0c;现在总结下&#xff1a; 1.netstat -anp |grep 端口号 如下&#xff0c;我以3306为例&#xff0c;netstat -anp |grep 3306&#xff08;此处备注下&#xff0c;我是以普通用户操作&…

深入理解synchronized底层原理

文章目录前言一、synchronized的特性1.1 原子性1.2 可见性1.3 有序性1.4 可重入性二、synchronized的用法三、synchronized锁的实现3.1 同步方法3.2 同步代码块四、synchronized锁的底层实现五、JVM对synchronized的优化5.1 锁膨胀5.1.1 偏向锁5.1.2 轻量级锁5.1.3 重量级锁5.2…

centos7重新加载服务的命令_CentOS7 从查看、启动、停止服务说起systemctl

执行命令“systemctl status 服务名.service”可查看服务的运行状态&#xff0c;其中服务名后的.service 可以省略&#xff0c;这是CenOS7以后采用systemd作为初始化进程后产生的变化。Systemctl是一个systemd工具&#xff0c;主要负责控制systemd系统和服务管理器。Systemd是一…

一体化住户调查_曲麻莱县2020年城乡一体化住户调查表彰会暨年报部署会

为全面推进我县城乡一体化住户调查工作&#xff0c;总结经验、鼓励优秀&#xff0c;提高统计员和辅助调查员的工作积极性&#xff0c;提高账本数据质量&#xff0c;11月25日下午&#xff0c;县统计局组织召开2020年全县城乡一体化住户调查工作表彰会暨年报部署会。全县6个镇的统…

电力系统继电保护第二版张保会_电力系统继电保护试题

一、填空题(每小题1分&#xff0c;共20分)1.电气元件配置两套保护&#xff0c;一套保护不动作时另一套保护动作于跳闸&#xff0c;称为_近后备_保护。2.电流继电器的_返回_电流与动作电流的比值称为继电器的返回系数。3.反应电流增大而瞬时动作的保护称为__无时限电流速断保护_…

Linux中shell脚本详解

文章目录1、第一个脚本程序&#xff1a;2、shell获取字符串长度&#xff1a;3、shell变量&#xff1a;4、**引用shell变量**&#xff1a;5、shell变量的赋值、修改、删除&#xff1a;5、shell特殊变量&#xff1a;6、shell中字符串的拼接&#xff1a;**7、字符串的截取**8、she…

java递归实现多级菜单栏_Java构建树形菜单以及支持多级菜单的实例代码

这篇文章主要介绍了Java构建树形菜单的实例代码(支持多级菜单),非常不错&#xff0c;具有参考借鉴价值&#xff0c;需要的朋友可以参考下效果图&#xff1a;支持多级菜单。菜单实体类&#xff1a;public class Menu {// 菜单idprivate String id;// 菜单名称private String nam…

java中clone方法的理解(深拷贝、浅拷贝)

文章目录前言&#xff1a;知识点一&#xff1a;什么是浅拷贝&#xff1f;知识点二&#xff1a;什么是深拷贝&#xff1f;知识点三、java拷贝&#xff08;clone&#xff09;的前提&#xff1a;知识点四&#xff1a;浅拷贝案例&#xff1a;拷贝类&#xff1a;测试类&#xff1a;总…