springboot整合minio8实现文件存储服务

2022-02-14 10:05:02

近期在构建公司技术服务平台,在搭建完一些基础技术服务之后,考虑到项目过程中使用到很多文件存储功能,所以在平台上面提供统一的文件存储服务。

一开始计划直接使用阿里云OSS服务(想法简单了),后来与CTO讨论后,决定使用MinIO来完成该服务。然后开始学习MinIO相关知识,各种查看中英文版文档,现在最新的中文文档版本还是MinIO7的,所以查看了官网的英文版文档:
MinIO文档

梳理下过程,将基础功能整理如下:

1. MinIO安装(docker单机)

前提需要了解docker及docker-compose相关知识。
windows本地使用MinIO,安装完docker及docker-compose后,
打开PowerShell,执行命令:

docker run -p 9000:9000 --name minio1 -v D:\data:/data -e “MINIO_ROOT_USER=phli” -e “MINIO_ROOT_PASSWORD=123456” minio/minio server /data

docker相关指令自行学习,本篇不做展开,将容器端口与宿主机端口做映射,文件存储目录挂载到本地D:\data目录下(容器内在/data下),设置MinIO root的user及password,minio/minio镜像。

本地MinIO启动成功,端口为9000。

2. 国际惯例,添加依赖

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

3. application配置
在这里插入图片描述
endpoint为本地MinIO服务url,accessKey及secretKey为MinIO设置的类似于userName/password。

4. MinIO配置类

@Data
@Component
public class MinIoClientConfig {
    @Value("${minio.endpoint}")
    private String endpoint;
    @Value("${minio.accessKey}")
    private String accessKey;
    @Value("${minio.secretKey}")
    private String secretKey;

    /**
     * 注入minio 客户端
     * @return
     */
    @Bean
    public MinioClient minioClient(){

        return MinioClient.builder()
                .endpoint(endpoint)
                .credentials(accessKey, secretKey)
                .build();
    }
}

通过@Bean注入minIO客户端。

5. MinIOUtil类,完成与MinIO的操作

@Component
public class MinIoUtil {
    @Autowired
    private MinioClient minioClient;

    /**
     * 查看存储bucket是否存在
     * @param bucketName 存储bucket
     * @return boolean
     */
    public Boolean bucketExists(String bucketName) {
        Boolean found;
        try {
            found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return found;
    }

    /**
     * 创建存储bucket
     * @param bucketName 存储bucket名称
     * @return Boolean
     */
    public Boolean makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 删除存储bucket
     * @param bucketName 存储bucket名称
     * @return Boolean
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 文件上传
     * @param file 文件
     * @param bucketName 存储bucket
     * @return Boolean
     */
    public Boolean upload(MultipartFile file, String bucketName) {
        try {
            PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(file.getOriginalFilename())
                    .stream(file.getInputStream(),file.getSize(),-1).contentType(file.getContentType()).build();
            //文件名称相同会覆盖
            minioClient.putObject(objectArgs);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 文件下载
     * @param bucketName 存储bucket名称
     * @param fileName 文件名称
     * @param res response
     * @return Boolean
     */
    public void download(String bucketName, String fileName, HttpServletResponse res) {
        GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName)
                .object(fileName).build();
        try (GetObjectResponse response = minioClient.getObject(objectArgs)){
            byte[] buf = new byte[1024];
            int len;
            try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()){
                while ((len=response.read(buf))!=-1){
                    os.write(buf,0,len);
                }
                os.flush();
                byte[] bytes = os.toByteArray();
                res.setCharacterEncoding("utf-8");
                //设置强制下载不打开
                res.setContentType("application/force-download");
                res.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
                try (ServletOutputStream stream = res.getOutputStream()){
                    stream.write(bytes);
                    stream.flush();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 查看文件对象
     * @param bucketName 存储bucket名称
     * @return 存储bucket内文件对象信息
     */
    public List<ObjectItem> listObjects(String bucketName) {
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).build());
        List<ObjectItem> objectItems = new ArrayList<>();
        try {
            for (Result<Item> result : results) {
                Item item = result.get();
                ObjectItem objectItem = new ObjectItem();
                objectItem.setObjectName(item.objectName());
                objectItem.setSize(item.size());
                objectItems.add(objectItem);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return objectItems;
    }

    /**
     * 批量删除文件对象
     * @param bucketName 存储bucket名称
     * @param objects 对象名称集合
     */
    public Iterable<Result<DeleteError>> removeObjects(String bucketName, List<String> objects) {
        List<DeleteObject> dos = objects.stream().map(e -> new DeleteObject(e)).collect(Collectors.toList());
        Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build());
        return results;
    }
}

MinIoUtil提供了创建存储bucket、删除存储bucket、判断存储bucket是否存在、文件的上传下载、查看bucket内文件对象、批量删除文件对象等功能(存储bucket及文件对象等的概念不做赘述,可查看minIO官方文档)。

6. Controller类

这儿不做赘述,自行编写即可。仅以文件上传为demo:

    @ApiOperation(value = "文件上传")
    @PostMapping("/upload")
    public Result upload(MultipartFile file, @RequestParam(value = "bucketName") String bucketName) {
        if (StringUtils.isBlank(bucketName)) {
            log.error("存储bucket名称为空,无法上传");
            return Result.fail("存储bucket名称为空,无法上传");
        }
        if (!minIoUtil.upload(file, bucketName)) {
            log.error("文件上传异常");
            return Result.fail("文件上传异常");
        }
        return Result.success("文件上传成功");
    }

然后就可以调用接口完成文件上传了,可以发现在本地及容器内均有所上传的文件。

补充说明:

MinIO在正式环境往往是分布式,分布式Minio可以让你将多块硬盘(甚至在不同的机器上)组成一个对象存储服务。由于硬盘分布在不同的节点上,分布式Minio避免了单点故障。

Docker Compose允许定义和运行单主机,多容器Docker应用程序。使用Compose,您可以使用Compose文件来配置MinIO服务。 然后,使用单个命令,您可以通过你的配置创建并启动所有分布式MinIO实例。 分布式MinIO实例将部署在同一主机上的多个容器中。 这是建立基于分布式MinIO的开发,测试和分期环境的好方法。

Docker Compose定义运行单主机,复杂场景可以考虑Kubernetes或者Docker Swarm,具体配置可查看MinIO官网文档。

  • 作者:冰红茶不会渴
  • 原文链接:https://blog.csdn.net/weixin_43241706/article/details/115351137
    更新时间:2022-02-14 10:05:02