springboot 集成 minio 对象存储

84 阅读2分钟

1. docker-compose安装

新建文件夹

mkdir -p /opt/app/minio && mkdir -p /opt/app/minio/data && mkdir -p /opt/app/minio/config

cd /opt/app/minio

vim docker-compose.yml

version: '3.0'
services: 
    minio:
      image: minio/minio
      container_name: minio
      restart: always
      ports:
        - 9000:9000
        - 9001:9001
      command: server /data --console-address ":9001"  #指定容器中的目录 /data
      environment:
        MINIO_ACCESS_KEY: minioadmin    #管理后台用户名
        MINIO_SECRET_KEY: minioadmin #管理后台密码,最小8个字符
      volumes:
        - ./data:/data              #映射当前目录下的data目录至容器内/data目录
        - ./config:/root/.minio/     #映射配置目录

访问控制台 http://ip:9001/

2. spirngboot

pom

<!--  minio 依赖  -->
<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>8.3.9</version>
</dependency>
<!--  minio 依赖于 okhttp  -->
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.9.0</version>
</dependency>

yml minio 配置

minio:
  endpoint: http://192.168.202.128:9000
  port: 9000
  accessKey: minioadmin
  secretKey: minioadmin
  secure: false
  bucket-name: xbb # 桶名
  image-size: 10485760 # 图片文件的最大大小
  file-size: 1073741824 # 文件的最大大小

MinioConfig

/**
 * Minio 配置
 */
@Data
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioConfig {
  /** 服务地址 */
  private String endpoint;

  /** TCP/IP端口号 */
  private Integer port;

  /** 账户 */
  private String accessKey;

  /** 密码 */
  private String secretKey;

  /** 如果是 true,则用的是 https 而不是 http,默认值是 true" */
  private boolean secure;

  /** 存储桶 */
  private String bucketName;

  /** 图片的最大大小 */
  private long imageSize;

  /** 其他文件的最大大小 */
  private long fileSize;

  /** 初始化 MinIO 客户端 */
  @Bean
  public MinioClient minioClient() {
    return MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();
  }
}

MinioService

@Slf4j
@Component
@RequiredArgsConstructor
public class MinioService {

  private final MinioConfig minioConfig;

  private final MinioClient minioClient;

  /** 文件上传 */
  public String upload(MultipartFile file) {
    String bucketName = minioConfig.getBucketName();
    try {
      if (!this.bucketExists(bucketName)) {
        this.makeBucket(bucketName);
      }
      String fileName = file.getOriginalFilename();
      assert fileName != null;
      String objectName =
          IdUtil.randomUUID().replace("-", "") + fileName.substring(fileName.lastIndexOf("."));
      this.putObject(bucketName, file, objectName, file.getContentType());
      return minioConfig.getEndpoint() + "/" + bucketName + "/" + objectName;
    } catch (Exception e) {
      log.error("文字上传失败", e);
      return null;
    }
  }

  /**
   * 检查存储桶是否存在
   *
   * @param bucketName 存储桶名称
   */
  @SneakyThrows
  public boolean bucketExists(String bucketName) {
    return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
  }

  /**
   * 创建存储桶
   *
   * @param bucketName 存储桶名称
   */
  @SneakyThrows
  public boolean makeBucket(String bucketName) {
    boolean flag = bucketExists(bucketName);
    if (!flag) {
      minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
      return true;
    } else {
      return false;
    }
  }

  /** 列出所有存储桶 */
  @SneakyThrows
  public List<Bucket> listBuckets() {
    return minioClient.listBuckets();
  }

  /**
   * 列出存储桶中的所有对象
   *
   * @param bucketName 存储桶名称
   */
  @SneakyThrows
  public Iterable<Result<Item>> listObjects(String bucketName) {
    boolean flag = bucketExists(bucketName);
    if (flag) {
      return minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build());
    }
    return null;
  }

  /**
   * 文件上传
   *
   * @param bucketName 桶名称
   * @param multipartFile 上传的文件
   */
  @SneakyThrows
  public void putObject(
      String bucketName, MultipartFile multipartFile, String filename, String fileType) {
    InputStream inputStream = new ByteArrayInputStream(multipartFile.getBytes());
    minioClient.putObject(
        PutObjectArgs.builder().bucket(bucketName).object(filename).stream(
                inputStream, -1, minioConfig.getFileSize())
            .contentType(fileType)
            .build());
  }
  
  /**
   * 获取对象的元数据
   *
   * @param bucketName 存储桶名称
   * @param objectName 存储桶里的对象名称
   */
  @SneakyThrows
  public StatObjectResponse statObject(String bucketName, String objectName) {
    boolean flag = bucketExists(bucketName);
    if (flag) {
      return minioClient.statObject(
          StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
    }
    return null;
  }

}

CommonController

@RestController
@RequestMapping("/common")
@Slf4j
public class CommonController {

  @Autowired private MinioService minioService;

  @PostMapping("/upload")
  public String upload(@RequestParam(name = "file") MultipartFile file) {
    return minioService.upload(file);
  }
}

3. 测试

postman请求上传图片后返回url, 图片不可访问

<?xml version="1.0" encoding="UTF-8"?>
<Error>
    <Code>AccessDenied</Code>
    <Message>Access Denied.</Message>
    <Key>7dc4086b79f04b6aa17a9c86568d5bf5.jpg</Key>
    <BucketName>xbb</BucketName>
    <Resource>/xbb/7dc4086b79f04b6aa17a9c86568d5bf5.jpg</Resource>
    <RequestId>17182108A38E78D8</RequestId>
    <HostId>4c4c25a2-6ca6-4504-9e6b-53d07bc1b774</HostId>
</Error>

登录minio控制台 ip:9000,修改 bucket 为 public 后,图片可正常访问。

image.png

github 代码 web模块

github.com/xiaogui1992…