SpringBoot集成Minio实现文件上传、下载

1,183 阅读5分钟

什么是Minio

Minio是GlusterFS创始人之一Anand Babu Periasamy发布新的开源项目,基于Apache License v2.0开源协议的对象存储项目,采用Golang实现,其客户端支持现主流的开发语言,如Java、javascript、Python以及Golang

安装Minio

Minio可以在K8S、Docker等容器中安装,也可以直接进行安装,或者是通过编译源码进行安装(该方式官方不推荐使用)

在容器中安装的方式可以参考官网的安装方式,这里不做介绍

这里要说的是在Linux(Ubuntu)中进行安装,参考地址:min.io/download#/l…

需注意的是,系统环境除了Windows之外,安装方式除了源码编译以及K8S安装,这三种情况下,其他的安装都需要确定系统的位数及架构(ARM或者是其他的),这里默认是 ARM 架构

第一步 安装

确定自己需要安装的路径,这里的安装路径 /usr/development/minio,系统自身并不存在目录,因此需要创建

$ cd /usr
$ mkdir development
$ cd development
$ mkdir minio

接下来根据官网所安装步骤安装即可,这里采用官网的 Binary 进行安装

$ wget https://dl.min.io/server/minio/release/linux-amd64/minio 

# 修改minio文件的读写权限
$ chmod +x minio

# 启动
$ MINIO_ROOT_USER=admin MINIO_ROOT_PASSWORD=password ./minio server /mnt/data --console-address ":9001"

当启动的时候,会惊奇的发现出现以下的报错信息,不要慌,按照报错的提示进行修改即可

image.png

在原启动命令的基础上,再加上 --address ":9000" 即可

出现这个报错主要是因为Minio需要两个启动端口,不指定的话,会随机使用两个端口作为启动端口的

  • --console-address:主要是minio的web管理界面的端口,可视化管理的
  • --address:主要是做了客户端访问的端口

相关命令解释

  • MINIO_ROOT_USER: Minio web管理界面的登录用户名
  • MINIO_ROOT_PASSWORD: Minio web管理界面的登录密码
  • ./minio: 当前目录下的minio文件
  • server: 开启服务
  • /mnt/data: 数据存储的目录

当看到以下信息说明已经启动完成了

image.png

这里可以看到,我这个报了 WARNING: Console endpoint is listening on a dynamic port (45979), please use --console-address ":PORT" to choose a static port.的错误

这是因为我这里只指定了客户端访问的端口,没有指定web管理界面的端口

需注意的是,虽然启动成功了,但是这种情况下,只要关闭终端窗口,Minio也会关闭掉

我们希望Minio后台运行,因此需要使用到 nohup命令

这里创建了一个启动脚本,方便运行 vim start.sh

nohup /usr/development/minio/minio server /usr/development/minio/data/ --address ":9000" --console-address ":9001" > /usr/development/minio/minio.log 2>&1 &

:wq 保存退出

在ubuntu使用的是 bash命令运行sh脚本,而在centos下则使用 sh运行

再度尝试访问Minio的web管理界面,发现需要输入账号跟密码,但启动脚本中并没有设置登录用户名和密码

官方早已想到了这一点,因此提供了一个默认的账号和密码:minioadmin:minioadmin

实际使用的过程中,并不会去使用默认的账号和密码

使用脚本命令启动服务的方式,可以通过设置环境变量实现设置默认的账号和密码

``

vim ~/.profile

export MINIO_ROOT_USER=test
export MINIO_ROOT_PASSWORD=test@123

这里需要注意的是,密码的长度最低是8位

:wq 保存退出,刷新一下环境变量 source ~/.profile,然后系统重启一下,再启动Minio

第二步 使用

首先需要创建一个 Bucket,翻译过来就是

image.png

image.png

输入 Bucket Name后点击 Create Bucket即可,其他的参数请自行了解

image.png

这样就得到了一个存储的

可点击右上角的 Upload 直接上传文件,这里不多做介绍

SpringBoot集成

创建一个SpringBoot项目

项目目录

image.png

pom.xml 依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
    </dependency>

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

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-io</artifactId>
        <version>1.3.2</version>
    </dependency>

</dependencies>

application.yml 配置文件

server:
  port: 8760

spring:
  application:
    name: minio-demo
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 10MB

minio:
  endpoint: http://192.168.71.131:9000
  accessKey: xiaomo
  secretKey: xiaomo@123
  bucketName: test-bucket

创建一个 Minio 配置文件 MinioProperties.java

@Data
@Configuration
@ConfigurationProperties(prefix = "minio")
public class MinioProperties {

    private String endpoint;

    private String accessKey;

    private String secretKey;

    private String bucketName;

}

MinioConfig.java

@Configuration
public class MinioConfig {

    private static final Logger LOGGER = LoggerFactory.getLogger(MinioConfig.class);

    @Resource
    private MinioProperties properties;

    @Bean
    public MinioClient minioClient() {
        return MinioClient.builder()
                .endpoint(properties.getEndpoint())
                .credentials(properties.getAccessKey(), properties.getSecretKey())
                .build();
    }

    public void initBucket() {
        LOGGER.info("bucket start create");
        MinioClient minioClient = minioClient();
        try {
            boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(properties.getBucketName()).build());
            if (!exists) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(properties.getBucketName()).build());
                LOGGER.info("bucket create success, bucket name >>> {} <<<", properties.getBucketName());
                return;
            }
            LOGGER.warn("bucket already exists, bucket name >>> {} <<<", properties.getBucketName());
        } catch (Exception e) {
            LOGGER.error("bucket create exception.", e);
        }
    }

}

创建 一个操作Minio的服务 MinioService.java

@Component
public class MinioService {
    private static final Logger LOGGER = LoggerFactory.getLogger(MinioService.class);

    @Resource
    private MinioConfig minioConfig;

    @Resource
    private MinioProperties properties;

    /** 根据File类进行上传 */
    public boolean upload(File file) {
        MinioClient minioClient = minioConfig.minioClient();
        String filename = file.getName();
        try {
            minioClient.uploadObject(UploadObjectArgs.builder()
                    .bucket(properties.getBucketName())
                    .object(filename)
                    .filename(file.getAbsolutePath())
                    .build());
            return true;
        } catch (Exception e) {
            LOGGER.error("file upload minio exception, file name: {}", filename, e);
            return false;
        }
    }
       
    /** 前端上传 */
    public boolean upload(MultipartFile file) {
        MinioClient minioClient = minioConfig.minioClient();
        String filename = file.getOriginalFilename();
        try {
            InputStream inputStream = file.getInputStream();
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(properties.getBucketName())
                    .object(filename)
                    .stream(inputStream, inputStream.available(), -1)
                    .contentType(file.getContentType())
                    .build());
            return true;
        } catch (Exception e) {
            LOGGER.error("file upload minio exception, file name: {}", filename, e);
            return false;
        }
    }

    /** 上传到Minio指定路径 */
    public boolean upload(String path, MultipartFile file) {
        MinioClient minioClient = minioConfig.minioClient();
        String filename = file.getOriginalFilename();
        try {
            filename = path + filename;
            InputStream inputStream = file.getInputStream();
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(properties.getBucketName())
                    .object(filename)
                    .stream(inputStream, inputStream.available(), -1)
                    .contentType(file.getContentType())
                    .build());
            return true;
        } catch (Exception e) {
            LOGGER.error("file upload minio exception, file name: {}", filename, e);
            return false;
        }
    }

    /** 上传到Minio指定目录,可选择自定义随机文件名 */
    public boolean upload(String path, MultipartFile file, Boolean isRandom) {
        MinioClient minioClient = minioConfig.minioClient();
        String filename = file.getOriginalFilename();
        if (Boolean.TRUE.equals(isRandom)) {
            long millis = System.currentTimeMillis();
            String extension = FilenameUtils.getExtension(filename);
            filename = millis + "." + extension;
        }
        try {
            filename = path + filename;
            InputStream inputStream = file.getInputStream();
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(properties.getBucketName())
                    .object(filename)
                    .stream(inputStream, inputStream.available(), -1)
                    .contentType(file.getContentType())
                    .build());
            return true;
        } catch (Exception e) {
            LOGGER.error("file upload minio exception, file name: {}, source file name: {}", filename, file.getOriginalFilename(), e);
            return false;
        }
    }
       
    /** response下载 */
    public void download(String filename, HttpServletResponse response) {
        MinioClient minioClient = minioConfig.minioClient();
        response.reset();
        try(InputStream inputStream = minioClient.getObject(GetObjectArgs.builder()
                .bucket(properties.getBucketName())
                .object(filename)
                .build())) {
            filename = filename.substring(filename.lastIndexOf("/") + 1);
            ServletOutputStream outputStream = response.getOutputStream();
            response.setContentType("application/octet-stream");
            response.setCharacterEncoding("utf-8");
            response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
            byte[] bytes = new byte[1024];
            int len;
            while ((len = inputStream.read(bytes)) > 0) {
                outputStream.write(bytes, 0, len);
            }
            outputStream.close();
        } catch (Exception e) {
            LOGGER.error("file download from minio exception, file name: {}", filename,  e);
        }
    }

}

需要注意一点是,Minio在同一个路径下若出现两个文件名一样的文件的时候,会自动覆盖掉上一版的文件,只会留下最新的版本,因此封装了一个自定义文件名称的方法

新建 HomeController.java

@RestController
public class HomeController {

    @Resource
    private MinioService minioService;

    @PostMapping("upload")
    public String upload(MultipartFile file) {
        boolean exists = minioService.upload("notes/",file, true);
        return exists ? "上传成功" : "上传失败";
    }

    @GetMapping("download")
    public void download(@RequestParam String filename, HttpServletResponse response) {
        minioService.download(filename, response);
    }
}

打开 PostMan,输入上传地址后,选择Body后将字段的类型选择为file,再从本地选择一张图片点击Send发送,响应成功

image.png

前往Minioweb管理端查看,点击 Refresh 出现一个目录,点击进行发现一个文件,说明已经成功上传了 image.png

image.png

文件下载HomeController已经写好了一个请求,请求方式是Get,所以直接打开浏览器,在地址栏输入请求地址,回车

image.png

这里为了方便,就直接在请求地址上输入文件在Minio全路径了

image.png

image.png

可以看到,成功的把图片下载下来了,

最后

若涉及到一些文件不想放到现有的对象存储服务上的话,可以使用Minio自行搭建一个文件管理系统

由于水平有限,若文章中有任何的错误,欢迎指正