Spring Boot Minio入门笔记

562 阅读4分钟

gitee链接

Spring Boot版本:2.3.4.RELEASE

minio是一个高性能对象存储服务,我们可以用它来实现文件的上传下载。

minio的官方教程绝对会更加详细,本文只是作为个人笔记备忘。

自从学习了docker,我几乎所有的环境都是容器,minio当然也不例外。

minio容器安装

docker run --name myminio -p 8789:9000 -itd --restart=always -v /etc/localtime:/etc/localtime -v /home/mycontainers/myminio/data:/data -v /home/mycontainers/myminio/config:/root/.minio --net mynetwork -e "MINIO_ACCESS_KEY=minio"  -e "MINIO_SECRET_KEY=minio123" minio/minio:RELEASE.2021-06-17T00-10-46Z server /data

指令说明:

  • --restart=always,容器随着docker的启动而启动
  • -p,端口映射,8789是我宿主机的限制端口,这个随意,9000是minio的默认端口
  • -v 路径A:路径B,映射宿主机的路径A到容器的路径B下,在上面这条指令中映射了3个路径,按顺序分别是1(保证宿主机的时间和容器内的时间一致);2(从宿主机可以快速访问到容器的文件存储路径,以及数据持久化);3(minio的配置持久化)
  • --net,网段
  • -e,minio的账密
  • minio/minio:RELEASE.2021-06-17T00-10-46Z,镜像版本
  • server,以server服务启动
  • /data,工作路径

docker的介绍不是本文重点,网上资料众多,相信有很多优秀的文章可以学习。

启动成功后,访问localhost:9000/minio可以看到可视化界面,操作易上手,可以多试试。

Maven依赖

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

配置文件

server:
  port: 8888

# minio配置
minio:
  endpoint: http://myserverhost:8789 # minio链接
  accessKey: minio  # minio的用户名
  secretKey: minio123 # minio的密码
  bucket: default  # 默认的文件存储桶名

配置类

MinioConfig:

package com.cc.config;

import io.minio.MinioClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

/**
 * minio配置类
 * @author cc
 * @date 2021-07-09 14:37
 */
@Configuration
public class MinioConfig {
    @Bean
    public MinioClient minioClient(MinioProperties properties){
        try {
            MinioClient.Builder builder = MinioClient.builder();
            builder.endpoint(properties.getEndpoint());
            if (StringUtils.hasLength(properties.getAccessKey()) && StringUtils.hasLength(properties.getSecretKey())) {
                builder.credentials(properties.getAccessKey(),properties.getSecretKey());
            }
            return builder.build();
        } catch (Exception e) {
            return null;
        }
    }
}

MinioProperties:

package com.cc.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * minio属性类,从配置文件中获取默认配置
 * @author cc
 * @date 2021-07-09 14:37
 */
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioProperties {
    /**
     * 对象存储服务的URL
     */
    private String endpoint;
    /**
     * Access key就像用户ID,可以唯一标识你的账户
     */
    private String accessKey;
    /**
     * Secret key是你账户的密码
     */
    private String secretKey;

    /**
     * 默认文件桶
     */
    private String bucket;

    ...
}

Service

编写MinioService:

package com.cc.service;

import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * minio的api调用
 * @author cc
 * @date 2021-07-09 10:53
 */
public interface MinioService {
    /**
     * 上传,返回文件名,然后通过文件名下载
     * @param file: 文件
     * @param bucket: 文件桶名
     * @param filename: 文件名
     * @return java.lang.String
     * @author cc
     * @date 2021-07-09 14:16
     */
    String upload(MultipartFile file, String bucket, String filename);

    /**
     * 下载
     * @param response:
     * @param bucket: 文件桶名
     * @param filename: 文件名
     * @return void
     * @author cc
     * @date 2021-07-09 14:16
     */
    void download(HttpServletResponse response, String bucket, String filename);

    /**
     * 删除
     * @param bucket: 文件桶名
     * @param filename: 文件名
     * @return java.lang.String
     * @author cc
     * @date 2021-07-09 14:16
     */
    String remove(String bucket, String filename);
    
    /**
     * 查询文件是否存在
     * @param bucket: 文件桶名
     * @param filename: 文件名
     * @return boolean
     * @author cc
     * @date 2021-07-09 14:16
     */
    boolean doesObjectExist(String bucket, String filename);
}

MinioServiceImpl:

package com.cc.service;

import com.cc.config.MinioProperties;
import io.minio.*;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;

/**
 * minio api官网链接:https://docs.min.io/docs/java-client-api-reference.html
 * @author cc
 * @date 2021-07-09 10:47
 */
@Service
public class MinioServiceImpl implements MinioService {
    private final MinioClient minioClient;
    private final MinioProperties properties;

    public MinioServiceImpl(MinioClient minioClient, MinioProperties properties) {
        this.minioClient = minioClient;
        this.properties = properties;
    }

    @Override
    public String upload(MultipartFile file, String bucket, String filename) {
        InputStream inputStream;
        if (StringUtils.isEmpty(bucket)) {
            bucket = properties.getBucket();
        }
        if (StringUtils.isEmpty(filename)) {
            filename = UUID.randomUUID().toString().replace("-", "") + "-" + file.getOriginalFilename();
        }
        try {
            // 是否有bucket,没有则创建
            boolean bucketExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());
            if (!bucketExist) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
            }
            inputStream = file.getInputStream();
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucket)
                    .object(filename).contentType(file.getContentType())
                    .stream(inputStream, inputStream.available(), -1)
                    .build());
        } catch (Exception e) {
            throw new RuntimeException("上传文件失败:" + e.getMessage());
        }
        try {
            inputStream.close();
        } catch (IOException e) {
            throw new RuntimeException("上传文件-inputStream关闭失败:" + e.getMessage());
        }
        return filename;
    }

    @Override
    public void download(HttpServletResponse response, String bucket, String filename) {
        InputStream inputStream;
        if (StringUtils.isEmpty(bucket)) {
            bucket = properties.getBucket();
        }
        try {
            inputStream = minioClient.getObject(GetObjectArgs.builder()
                    .bucket(bucket)
                    .object(filename)
                    .build());
            IOUtils.copy(inputStream, response.getOutputStream());
        } catch (Exception e) {
            throw new RuntimeException("下载文件失败:" + e.getMessage());
        }
        try {
            inputStream.close();
        } catch (IOException e) {
            throw new RuntimeException("下载文件-inputStream关闭失败:" + e.getMessage());
        }
    }

    @Override
    public String remove(String bucket, String filename) {
        if (bucket == null || bucket.length() == 0) {
            bucket = properties.getBucket();
        }
        if (!doesObjectExist(bucket, filename)) {
            throw new RuntimeException("文件不存在,无法删除,请检查文件名和存储桶");
        }
        try {
            minioClient.removeObject(
                    RemoveObjectArgs.builder().bucket(properties.getBucket()).object(filename).build());
        } catch (Exception e) {
            throw new RuntimeException("删除文件失败:" + e.getMessage());
        }
        return String.format("删除文件[%s]成功", filename);
    }

    @Override
    public boolean doesObjectExist(String bucket, String filename) {
        if (bucket == null || bucket.length() == 0) {
            bucket = properties.getBucket();
        }
        try {
            minioClient.statObject(StatObjectArgs.builder()
                    .bucket(bucket)
                    .object(filename).build());
        } catch (Exception e) {
            return false;
        }
        return true;
    }
}

编写接口Controller来测试

MinioController:

package com.cc.controller;

import com.cc.service.MinioServiceImpl;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;

@RestController
public class MinioController {
    private final MinioServiceImpl minioService;

    public MinioController(MinioServiceImpl minioService) {
        this.minioService = minioService;
    }

    // 文件上传
    @PostMapping("/upload")
    public void upload(@RequestParam("file") MultipartFile file) {
        minioService.upload(file, null, null);
    }

    // 文件下载
    @GetMapping("/download")
    public void download(HttpServletResponse response, @RequestParam("bucket") String bucket, @RequestParam("filename") String filename) {
        minioService.download(response, bucket, filename);
    }

    // 文件删除
    @GetMapping("/remove")
    public void remove(@RequestParam("bucket") String bucket, @RequestParam("filename") String filename) {
        minioService.remove(bucket, filename);
    }

    // 查询文件是否存在
    @GetMapping("/find")
    public Boolean find(@RequestParam("bucket") String bucket, @RequestParam("filename") String filename) {
        return minioService.doesObjectExist(bucket, filename);
    }
}

代码编写完成,启动程序即可进行测试。