分布式文件存储系统Minio之环境搭建及Springboot集成使用

关于Minio分布式文件存储系统是什么,后期做一个知识分享。另外,Linux系统自己也不是很熟,所以对于用到的一些小白知识也会做一个简单的介绍。

Minio支持Windows、MacOs以及Linux常用系统的部署,为了更加贴合生产环境,这里我以Linux系统为例,介绍一下Minio的部署方式以及如何集成到我们的项目当中。

minio 初探

作为入门,我们先简单搭建一个单机环境,并且在项目中进行使用。

单机服务部署

参照官方文档docs.minio.org.cn/docs/,将minio可执行文件下载下来,放到/opt目录下(版本为20201028,minio的更新非常快)。执行以下命令:

#赋予文件执行权限
chmod +x minio
#启动服务
./minio server /data
复制代码
官网下载有点慢,可以到国内镜像下载 http://dl.minio.org.cn/
复制代码

出现下图即说明部署成功,minio提供浏览器端的使用方式,你可以访问http://127.0.0.1:9000,输入accesskey和secretkey(默认是minioadmin,也可以自己配置)登陆minio的浏览器管理界面,来使用minio。9000是minio的默认端口,也可以自己指定端口。

#设置minio accesskey(关于export 命令可以参考[主要用来设置环境变量](https://www.runoob.com/linux/linux-comm-export.html))
export MINIO_ACCESS_KEY=yourAccesskey
#设置minio secretKey
export MINIO_SECRET_KEY=yourSecretKey
#指定端口
./minio  server /data --address :9001
复制代码

浏览器端食用

minio浏览器端使用界面,minio提供的管理功能如图所示,主要包括创建存储桶和文件上传、下载、分享、删除操作。值得一提的是minio的文件分享和百度云的分享类似,可以设置分享链接的有效期(视自己的应用场景使用)。

OK,至此一个单机的minio服务已经成功部署,接下来,让我们在自己的项目中使用minio来进行文件的一些常规操作。

Springboot 食用

为什么使用springboot来介绍minio在项目中的使用呢?因为它方便、快、简单!

依赖包

我使用的是开发工具是idea,新建springboot项目,在pom文件添加如下依赖(springboot、minio、lombok)。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>8.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
复制代码

minio配置类

主要是装载MinioClient对象,这是Java使用minio的客户端对象,所有涉及minio的操作都要借助它来完成。 首先,将minio的相关参数写入到配置文件application.yml

#minio 参数配置(accessKey、secretKey、访问地址、默认存储桶)
minio:
  endpoint: http://127.0.0.1:9000
  accesskey: minioadmin
  secretKey: minioadmin
  bucketName: bucket1
复制代码

Minio配置类如下:

@Configuration
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MinioConfig {
    @Value("${minio.endpoint}")
    String endpoint;
    @Value("${minio.accessKey}")
    String accessKey;
    @Value("${minio.secretKey}")
    String secretKey;
    @Value("${minio.bucketName}")
    String bucketName;

    @Bean
    public MinioClient minioClient() {
        //新建minioClient,之后的所有minio有关操作都需使用该对象
        MinioClient minioClient =
                MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();
        //判断默认存储桶是否存在,若不存在,调用接口进行创建
        try {
            //调用bucketExists接口,判断存储桶是否已经存在
            if (minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()))
                return minioClient;
            else
                //创建默认的存储桶
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        } catch (ErrorResponseException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return minioClient;
    }
}
复制代码

minio工具类

这里只简单介绍一下常用的几个功能,包括存储桶的创建、文件上传、下载等。完整工具类见github。 判断存储桶是否存在和创建存储桶,上面已经使用过了,这里就不再说了。文件上传方法如下,当文件上传成功之后,我们需要记得文件上传到哪个存储桶以及文件的上传路径,这样才可以下载文件。

public static String uploadFile(String bucketName, String fileName, InputStream inputStream) {
        //如果bucketName 为空,则使用默认的存储桶
        if (StringUtils.isEmpty(bucketName)) {
            bucketName = defaultBucketName;
        }
        String filePath = null;
        try {
//            当文件名重复时,minio默认会覆盖原文件,所以这里为文件名添加一个随机生成的前缀(如果想不被覆盖,可以开启存储桶的版本设置)
            fileName= RandomStringUtils.randomNumeric(8)+"/"+fileName;
//            调用minio的上传接口,上传文件,并指定上传以后的文件路径(也就是文件名)
            minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).
                    object(fileName).
                    stream(inputStream, -1, 50 * 1024 * 1024).
                    build());
            filePath=fileName;
        } catch (Exception e) {
            e.printStackTrace();
        }
        //返回文件在存储桶的存储路径
        return filePath;
    }
复制代码

文件下载方法如下,传入文件所在的存储桶和文件的存储路径,即可下载文件。

public static InputStream downLoadFile(String bucketName,String filePath){
        //如果bucketName 为空,则使用默认的存储桶
        if (StringUtils.isEmpty(bucketName)) {
            bucketName = defaultBucketName;
        }
        InputStream inputStream=null;
        try {
//            调用minio的下载接口,传入要下载文件路径,获取文件流
            inputStream=
                    minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(filePath).build());
        }catch (Exception e) {
            e.printStackTrace();
        }
        return inputStream;
    }
复制代码

minio 测试使用

新建springboot工程

工程目录结构如下:

image-20220105213646150

pom文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.crossnote</groupId>
    <artifactId>miniodemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>miniodemo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.4.RELEASE</spring-boot.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>8.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.1</version>
        </dependency>


    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.4.RELEASE</version>
                <configuration>
                    <mainClass>com.crossnote.miniodemo.MiniodemoApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

复制代码

application.yml内容如下:

server:
  port: 8081
  servlet:
    # 服务访问路径
    context-path: /minioserver
spring:
  application:
    name: minio-spring
  servlet:
    multipart:
      #最大上传文件设置
      max-file-size: 5120MB
      max-request-size: 5120MB

#minio 参数配置(accessKey、secretKey、访问地址、默认存储桶)
minio:
  endpoint: http://127.0.0.1:9000
  accessKey: minioadmin
  secretKey: minioadmin
  bucketName: bucket1
复制代码

编写一个简单的controller,包含文件上传和下载两个接口:

@RestController
public class MinioController {


    /**
     * 文件上传,返回文件在存储桶中的存储路径
     * @param file
     * @return 存储路径
     */
    @PostMapping("/uploadFile")
    public String uploadFile(MultipartFile file) {
        String fileName = file.getOriginalFilename();
        InputStream inputStream = null;
        try {
            inputStream = file.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return MinioUtil.uploadFile(null, fileName, inputStream);
    }

    /**
     * 文件下载接口,返回文件流
     * @param filePath
     * @param response
     */
    @GetMapping("/downloadFile")
    public void downLoadFile(String filePath, HttpServletResponse response) {
        InputStream fileInputStream = MinioUtil.downLoadFile(null, filePath);
        OutputStream outputStream = null;
        try {
            outputStream = response.getOutputStream();
            IOUtils.copyLarge(fileInputStream, outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


}
复制代码

记录一下写MinioUtil类遇到的知识点:

一开始,这个工具类我是这么写的,看着没啥问题,MinioUtil是个工具类,方法都用static描述,直接在Controller中调用,但是调用上传接口时报 59行 bucketName为null。 why?检查一下@Value注入参数确实是存在的,想起来static对象和普通对象初始化的顺序不同(不清楚底层是不是这个原因),参照这个进行改正

public class MinioUtil {

    //注入minioClient对象
    @Autowired
    private static MinioClient minioClient;
    @Value("${minio.bucketName}")
    private static String defaultBucketName;


    /**
     * 判断存储桶是否存在
     *
     * @param bucketName 存储桶名称
     * @return
     */
    public static boolean isBucketExists(String bucketName) {
        try {
            return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

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

    /**
     * 上传文件,并返回文件的存储路径
     *
     * @param bucketName  存储桶名称
     * @param fileName    文件名
     * @param inputStream 文件流
     * @return
     */
    public static String uploadFile(String bucketName, String fileName, InputStream inputStream) {
        //如果bucketName 为空,则使用默认的存储桶
        if (StringUtils.isEmpty(bucketName)) {
            bucketName = defaultBucketName;
        }
        String filePath = null;
        try {
//            当文件名重复时,minio默认会覆盖原文件,所以这里为文件名添加一个随机生成的前缀(如果想不被覆盖,可以开启存储桶的版本设置)
            fileName = RandomStringUtils.randomNumeric(8) + "/" + fileName;
//            调用minio的上传接口,上传文件,并指定上传以后的文件路径(也就是文件名)
            minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).
                    object(fileName).
                    stream(inputStream, -1, 50 * 1024 * 1024).
                    build());
            filePath = fileName;
        } catch (Exception e) {
            e.printStackTrace();
        }
        //返回文件在存储桶的存储路径
        return filePath;
    }

    /**
     * 下载文件,返回文件流
     *
     * @param bucketName 存储桶名称
     * @param filePath   文件路径(对minio来说,就是文件名)
     * @return
     */
    public static InputStream downLoadFile(String bucketName, String filePath) {
        //如果bucketName 为空,则使用默认的存储桶
        if (StringUtils.isEmpty(bucketName)) {
            bucketName = defaultBucketName;
        }
        InputStream inputStream = null;
        try {
//            调用minio的下载接口,传入要下载文件路径,获取文件流
            inputStream =
                    minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(filePath).build());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return inputStream;
    }
}
复制代码

改正后的写法如下:

@Component
public class MinioUtil {

    private static MinioClient minioClient;
    private static String defaultBucketName;

   /**
     * https://blog.csdn.net/mononoke111/article/details/81088472
     * 若要给静态变量赋值,可以使用set()方法,其中需要在类上加入@Component注解
     */
    @Value("${minio.bucketName}")
    public void setDefaultBucketName(String defaultBucketName) {
        MinioUtil.defaultBucketName = defaultBucketName;
    }

    @Autowired
    public void setMinioClient(MinioClient minioClient) {
        MinioUtil.minioClient = minioClient;
    }

    /**
     * 判断存储桶是否存在
     *
     * @param bucketName 存储桶名称
     * @return
     */
    public static boolean isBucketExists(String bucketName) {
        try {
            return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

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

    /**
     * 上传文件,并返回文件的存储路径
     *
     * @param bucketName  存储桶名称
     * @param fileName    文件名
     * @param inputStream 文件流
     * @return
     */
    public static String uploadFile(String bucketName, String fileName, InputStream inputStream) {
        //如果bucketName 为空,则使用默认的存储桶
        if (StringUtils.isEmpty(bucketName)) {
            bucketName = defaultBucketName;
        }
        String filePath = null;
        try {
//            当文件名重复时,minio默认会覆盖原文件,所以这里为文件名添加一个随机生成的前缀(如果想不被覆盖,可以开启存储桶的版本设置)
            fileName = RandomStringUtils.randomNumeric(8) + "/" + fileName;
//            调用minio的上传接口,上传文件,并指定上传以后的文件路径(也就是文件名)
            minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).
                    object(fileName).
                    stream(inputStream, -1, 50 * 1024 * 1024).
                    build());
            filePath = fileName;
        } catch (Exception e) {
            e.printStackTrace();
        }
        //返回文件在存储桶的存储路径
        return filePath;
    }

    /**
     * 下载文件,返回文件流
     *
     * @param bucketName 存储桶名称
     * @param filePath   文件路径(对minio来说,就是文件名)
     * @return
     */
    public static InputStream downLoadFile(String bucketName, String filePath) {
        //如果bucketName 为空,则使用默认的存储桶
        if (StringUtils.isEmpty(bucketName)) {
            bucketName = defaultBucketName;
        }
        InputStream inputStream = null;
        try {
//            调用minio的下载接口,传入要下载文件路径,获取文件流
            inputStream =
                    minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(filePath).build());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return inputStream;
    }
}
复制代码

开启服务之后,调用上传接口,返回文件的存储路径:

image-20220105222347919

使用上传借口返回的上传路径,下载文件(直接从浏览器打开):

http://localhost:8081/minioserver/downloadFile?filePath=15089295/SpringBoot 从入门到进阶系列官方小册.pdf

猜你喜欢

转载自juejin.im/post/7049731591211319333