【OSS对象存储】- 基于MinIO的文件存储

对象存储服务(Object Storage Service,简称 OSS),以 HTTP RESTful API 的形式对外提供服务,提供的海量、安全、低成本、高可靠的云存储服务,适合存放任意类型的文件。本文介绍基于基于MinIO的文件存储实现。

版本:

  • JDK 11
  • Spring boot 2.6.9
  • io.minio 8.4.1

配置类

@Data
@ConfigurationProperties("alkaid.oss.minio")
public class MinioProperties {
    /** 服务器参数 */
    private Server server = new Server();
    /** 连接参数 */
    private Connection connection = new Connection();
    /** 请求方法 */
    private Method method = new Method();

    @Data
    public static class Method {
        /**
         * 单位:秒
         */
        private Duration postExpiry = Duration.ofDays(7);
        /**
         * 单位:秒
         */
        private Duration getExpiry = Duration.ofDays(7);
        /**
         * 单位:秒
         */
        private Duration deleteExpiry = Duration.ofDays(7);
    }

    @Data
    public static class Connection {

        /**
         * Define the connect timeout for the Minio Client.
         */
        private Duration connectTimeout = Duration.ofSeconds(10);

        /**
         * Define the write timeout for the Minio Client.
         */
        private Duration writeTimeout = Duration.ofSeconds(60);

        /**
         * Define the read timeout for the Minio Client.
         */
        private Duration readTimeout = Duration.ofSeconds(10);
    }

    @Data
    public static class Server {

        /**
         * URL for Minio instance. Can include the HTTP scheme. Must include the port.
         * If the port is not provided, then the port of the HTTP is taken.
         */
        private String endpoint;

        private int port;

        /**
         * Access key (login) on Minio instance
         */
        private String accessKey;

        /**
         * Secret key (password) on Minio instance
         */
        private String secretKey;

        /**
         * If the scheme is not provided in {@code url} property, define if the
         * connection is done via HTTP or HTTPS.
         */
        private boolean secure = false;
    }
     
}

核心服务

  • GetOptService

/**
 * get操作
 *  
 * @author Lucky Yang
 * @since 0.0.1
 */
@Slf4j
@Service
public class GetOptService {
    @Autowired
    private MinioClient client;
    @Autowired
    private MinioProperties properties;

    /**
     * 获取对象流
     * 
     * @param bucket
     * @param objectId
     * @param consumer
     * @return
     */
    public InputStream getObject(final String bucket, final String objectId, final Consumer<GetObjectArgs.Builder> consumer) {
        final String minioId = MinioUtils.minioId(objectId);
        try {
            final GetObjectArgs.Builder builder = GetObjectArgs.builder()
                    .bucket(bucket)
                    .object(minioId);

            if (consumer != null) {
                consumer.accept(builder);
            }

            final GetObjectArgs args = builder.build();
            if (log.isDebugEnabled()) {
                log.debug("GetObjectArgs={}", MinioArgsUtils.printGetObjectArgs(args));
            }

            return client.getObject(builder.build());
        } catch (Exception e) {
            throw MinioExceptionUtil.wapper(e);
        }
    }

    /**
     * 获取对象流
     * 
     * @param bucket
     * @param objectId
     * @return
     */
    public InputStream getObject(final String bucket, final String objectId) {
        return getObject(bucket, objectId, null);
    }

    /**
     * 获取对象预签名URL
     * 
     * @param bucket
     * @param objectId
     * @param method
     * @param consumer
     * @return
     */
    public String getPresignedObjectUrl(final String bucket, final String objectId, final Method method,
            final Consumer<GetPresignedObjectUrlArgs.Builder> consumer) {
        final String minioId = MinioUtils.minioId(objectId);
        try {
            final GetPresignedObjectUrlArgs.Builder builder = GetPresignedObjectUrlArgs.builder()
                    .bucket(bucket)
                    .object(minioId)
                    .method(method);

            /** 使用属性配置数据 */
            switch (method) {
                case GET:
                    builder.expiry((int) properties.getMethod().getGetExpiry().toSeconds());
                    break;
                case POST:
                    builder.expiry((int) properties.getMethod().getPostExpiry().toSeconds());
                    break;
                case DELETE:
                    builder.expiry((int) properties.getMethod().getDeleteExpiry().toSeconds());
                    break;
                default:
                    break;
            }

            if (consumer != null) {
                consumer.accept(builder);
            }

            final GetPresignedObjectUrlArgs args = builder.build();
            if (log.isDebugEnabled()) {
                log.debug("GetPresignedObjectUrlArgs={}", MinioArgsUtils.printGetPresignedObjectUrlArgs(args));
            }
            return client.getPresignedObjectUrl(args);
        } catch (Exception e) {
            throw MinioExceptionUtil.wapper(e);
        }
    }

    /**
     * 获取对象预签名URL
     * 
     * @param bucket
     * @param objectId
     * @param method 请求方法,包括:POST, GET, PUT, DELETE
     * @return
     */
    public String getPresignedObjectUrl(final String bucket, final String objectId, final Method method) {
        return getPresignedObjectUrl(bucket, objectId, method, null);
    }    
}
  • ListOptsService

/**
 * list操作
 *  
 * @author Lucky Yang
 * @since 0.0.1
 */
@Slf4j
@Service
public class ListOptsService {
    @Autowired
    private MinioClient client;

    /**
     * 获取对象信息
     * 
     * @param bucket
     * @param consumer
     * @return
     */
    public List<Item> listObjects(final String bucket, final Consumer<ListObjectsArgs.Builder> consumer) {
        final ListObjectsArgs.Builder builder = ListObjectsArgs.builder()
                .bucket(bucket)
                .recursive(false);

        if (consumer != null) {
            consumer.accept(builder);
        }

        final ListObjectsArgs args = builder.build();

        if (log.isDebugEnabled()) {
            log.debug("ListObjectsArgs={}", MinioArgsUtils.printListObjectsArgs(args));
        }

        return convert(client.listObjects(args));
    }

    /**
     * 转换对象
     * 
     * @param items
     * @return
     */
    private List<Item> convert(Iterable<Result<Item>> items) {
        return StreamSupport
                .stream(items.spliterator(), true)
                .map(itemResult -> {
                    try {
                        return itemResult.get();
                    } catch (Exception e) {
                        throw MinioExceptionUtil.wapper(e);
                    }
                })
                .collect(Collectors.toList());
    }   
}
  • PutOptService

/**
 * put操作
 *  
 * @author Lucky Yang
 * @since 0.0.1
 */
@Slf4j
@Service
public class PutOptService {
    @Autowired
    private MinioClient client;

    /**
     * 上传一个文件流
     * 
     * @param bucket
     * @param objectId
     * @param file        文件流
     * @param contentType
     * @param consumer
     */
    public void putObject(final MinioUserMetadata metadata, final InputStream file, final Consumer<PutObjectArgs.Builder> consumer) {
        final String minioId = MinioUtils.minioId(metadata.getObjectId());
        metadata.setMinioId(minioId);

        try {
            final PutObjectArgs.Builder builder = PutObjectArgs.builder()
                    .bucket(metadata.getBucket())
                    .object(metadata.getMinioId())
                    .userMetadata(metadata.toMap())
                    .stream(file, file.available(), -1)
                    .contentType(metadata.getContentType());
            if (consumer != null) {
                consumer.accept(builder);
            }

            final PutObjectArgs args = builder.build();
            if (log.isDebugEnabled()) {
                log.debug("PutObjectArgs={}", MinioArgsUtils.printPutObjectArgs(args));
            }
            client.putObject(args);
        } catch (Exception e) {
            throw MinioExceptionUtil.wapper(e);
        }
    }

    /**
     * 上传一个文件流
     * @param metadata
     * @param file
     */
    public void putObject(final MinioUserMetadata metadata, final InputStream file) {
        this.putObject(metadata, file, null);
    }   
}

避免将所有文件存储到根目录下,程序自动生成两级目录,参考MinioUtils.minioId方法

  • RemoveOptService

/**
 * remove操作
 *  
 * @author Lucky Yang
 * @since 0.0.1
 */
@Slf4j
@Service
public class RemoveOptService {
    @Autowired
    private MinioClient client;

    /**
     * 删除对象
     * 
     * @param bucket
     * @param objectId
     * @param consumer
     */
    public void removeObject(final String bucket, final String objectId, final Consumer<RemoveObjectArgs.Builder> consumer) {
        final String minioId = MinioUtils.minioId(objectId);
        try {
            final RemoveObjectArgs.Builder builder = RemoveObjectArgs.builder()
                    .bucket(bucket)
                    .object(minioId);

            if (consumer != null) {
                consumer.accept(builder);
            }

            final RemoveObjectArgs args = builder.build();
            if (log.isDebugEnabled()) {
                log.debug("RemoveObjectArgs={}", MinioArgsUtils.printRemoveObjectArgs(args));
            }

            client.removeObject(args);
        }catch (Exception e) {
            throw MinioExceptionUtil.wapper(e);
        }
    }

    /**
     * 删除对象
     * 
     * @param bucket
     * @param objectId
     */
    public void removeObject(final String bucket, final String objectId) {
        removeObject(bucket, objectId, null);
    }    
}
  • StatOptService

/**
 * stat操作
 *  
 * @author Lucky Yang
 * @since 0.0.1
 */
@Slf4j
@Service
public class StatOptService {
    @Autowired
    private MinioClient client;

    /**
     * 对象是否存在
     * 
     * @param bucket
     * @param objectId
     * @return
     */
    public boolean exist(final String bucket, final String objectId) {
        boolean found = false;
        try {
            StatObjectResponse response = statObject(bucket, objectId);
            found = MinioUtils.minioId(objectId).equals(response.object());
        }catch(MinioOssException e) { 
            if (! (e instanceof NoSuchObjectException || e instanceof NoSuchKeyException)) {
                throw e;
            }
        }

        return found;
    }

    /**
     * 获取单个对象元数据
     * 
     * @param bucket
     * @param objectId
     * @param consumer
     * @return
     */
    public MinioUserMetadata getUserMetadata(final String bucket, final String objectId,
            final Consumer<StatObjectArgs.Builder> consumer) {
        final StatObjectResponse statObjectResponse = this.statObject(bucket, objectId, consumer);
        return MinioUserMetadata.of(statObjectResponse.userMetadata());
    }

    /**
     * 获取单个对象元数据
     * 
     * @param bucket
     * @param objectId
     * @return
     */
    public MinioUserMetadata getUserMetadata(final String bucket, final String objectId) {
        final StatObjectResponse statObjectResponse = this.statObject(bucket, objectId);
        return MinioUserMetadata.of(statObjectResponse.userMetadata());
    }

    /**
     * 获取多个对象元数据
     * 
     * @param bucket
     * @param objectIds
     * @return
     */
    public List<MinioUserMetadata> getUserMetadata(final String bucket, final Iterable<String> objectIds) {
        return StreamSupport.stream(objectIds.spliterator(), false)
                .map(objectId -> getUserMetadata(bucket, objectId))
                .collect(Collectors.toList());
    }

    /**
     * 获取单个对象状态
     * 
     * @param bucket
     * @param objectId
     * @param consumer
     * @return
     */
    public StatObjectResponse statObject(final String bucket, final String objectId,
            final Consumer<StatObjectArgs.Builder> consumer) {
        final String minioId = MinioUtils.minioId(objectId);
        try {
            final StatObjectArgs.Builder builder = StatObjectArgs.builder()
                    .bucket(bucket)
                    .object(minioId);
            if (consumer != null) {
                consumer.accept(builder);
            }

            final StatObjectArgs args = builder.build();
            if (log.isDebugEnabled()) {
                log.debug("StatObjectArgs={}", MinioArgsUtils.printStatObjectArgs(args));
            }
            return client.statObject(args);
        } catch (Exception e) {
            throw MinioExceptionUtil.wapper(e);
        }
    }

    /**
     * 获取单个对象状态
     * 
     * @param bucket
     * @param objectId
     * @return
     */
    public StatObjectResponse statObject(final String bucket, final String objectId) {
        return statObject(bucket, objectId, null);
    }

    /**
     * 获取多个对象状态
     * 
     * @param bucket
     * @param objectIds
     * @return
     */
    public Map<String, StatObjectResponse> statObject(final String bucket,
            final Iterable<String> objectIds) {
        return StreamSupport.stream(objectIds.spliterator(), false)
                .map(objectId -> new HashMap.SimpleEntry<>(objectId, statObject(bucket, objectId)))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    }    
}

以上是核心代码,完整的项目可以在Gitee​​​​​​​找到,欢迎下载。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值