微服务整合minio分布式存储(docker部署使用minio)

微服务整合minio分布式存储(docker部署使用minio)

前言: MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,如照片,视频,日志文件,备份和容器/ VM映像。对象的大小可以从几KB到最大5TB。

MinIO是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或者 MySQL。

一. docker部署minio
  1. 拉取最新minio镜像
docker pull minio/minio:latest
  1. Docker 启动Minio镜像(推荐:官方文档
docker run \
  -p 9000:9000 \
  -p 9001:9001 \
  --name minio1 \
  -v /mnt/data:/data \
  -e "MINIO_ROOT_USER=minioadmin" \
  -e "MINIO_ROOT_PASSWORD=minioadmin" \
  quay.io/minio/minio server /data --console-address ":9001"
  1. 开放端口浏览器进行访问:http://ip:9000 (账号密码:minioadmin)
    在这里插入图片描述
二.SpringBoot整合minio进行文件上传
1. 导入最新依赖
<!-- 分布式对象存储 -->
<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>7.1.0</version>
</dependency>
2. 项目中minio配置
minio.endpoint=http://ip:9000
minio.port=9000
minio.access-key=minioadmin
minio.secret-key=minioadmin
minio.secure=false
minio.bucket-name=commons
minio.image-size=10485760
minio.file-size=1073741824

minio.url=https://域名/
3. 实现代码如下:
  • 引用minio配置内容

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

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

    private String endpoint;

    private Integer port;

    private String accessKey;

    private String secretKey;

    private String url;

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

    /**
     *     //"默认存储桶"
     */
    private String bucketName;

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

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

}

  • minio service层方法实现 (注意设置桶的policy策略,不然会出问题,如下),文件路径是进行拼接的
import cn.hutool.core.lang.Assert;
import io.minio.*;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

@Slf4j
@Component
@EnableConfigurationProperties({MinIOProperties.class})
public class MinIOService implements InitializingBean {

    private MinIOProperties minIOProperties;

    public MinIOService(MinIOProperties minIOProperties){
        this.minIOProperties=minIOProperties;
    }

    private MinioClient client;

    @Override
    public void afterPropertiesSet() {
        Assert.notBlank(minIOProperties.getEndpoint(), "MinIO URL 为空");
        Assert.notBlank(minIOProperties.getAccessKey(), "MinIO accessKey为空");
        Assert.notBlank(minIOProperties.getSecretKey(), "MinIO secretKey为空");
        this.client = new MinioClient.Builder()
                .credentials(minIOProperties.getAccessKey(), minIOProperties.getSecretKey())
                .endpoint(minIOProperties.getEndpoint(),minIOProperties.getPort(),minIOProperties.getSecure())
                .build();
    }

    /**
     * 检查存储桶是否存在
     *
     * @param bucketName 存储桶名称
     * @return
     */
    @SneakyThrows
    public boolean bucketExists(String bucketName) {
        boolean found = client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (found) {
            System.out.println(bucketName + " exists");
        } else {
            System.out.println(bucketName + " does not exist");
        }
        return found;
    }

    @SneakyThrows
    public void createBucketIfAbsent(String bucketName) {
        System.out.println(bucketExists(bucketName));
        if (!bucketExists(bucketName)) {
            MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder()
                    .bucket(bucketName).build();
            client.makeBucket(makeBucketArgs);
            //设置桶的policy策略
            client.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(MinioPolicyConstant.READ_WRITE.replace(MinioPolicyConstant.BUCKET_PARAM,
                    bucketName)).build());
            System.out.println("创建成功");
        }
    }

    public String putObject(String bucketName, String objectName,  MultipartFile file) throws Exception {
        createBucketIfAbsent(bucketName);
        PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
//                .contentType(MediaType.ALL_VALUE)
                .contentType(file.getContentType())
                .stream(file.getInputStream(), file.getInputStream().available(), -1)
                .build();
        client.putObject(putObjectArgs);
//        String path = client.getObjectUrl(bucketName, objectName);
//        String path = GlobalConstants.FILR_HEAD_UTL +bucketName+"/"+objectName;
        String path = minIOProperties.getUrl() +bucketName+"/"+objectName;
        return path;
    }

    public void removeObject(String bucketName, String objectName) throws Exception {
        RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .build();
        client.removeObject(removeObjectArgs);
    }
}

  • 配置桶的policy策略
public class MinioPolicyConstant {


    public static final String BUCKET_PARAM = "${bucket}";

    /**
     * bucket权限-只读
     */
    public static final String READ_ONLY = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucket\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetObject\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "/*\"]}]}";
    /**
     * bucket权限-只读
     */
    public static final String WRITE_ONLY = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucketMultipartUploads\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:AbortMultipartUpload\",\"s3:DeleteObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "/*\"]}]}";
    /**
     * bucket权限-读写
     */
    public static final String READ_WRITE = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucket\",\"s3:ListBucketMultipartUploads\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:DeleteObject\",\"s3:GetObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\",\"s3:AbortMultipartUpload\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "/*\"]}]}";


}

  • 最后控制层接口引用及接口测试

import cn.hutool.core.util.IdUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

@Api(tags = "文件接口")
@RestController
@RequestMapping("/v1/files")
@Slf4j
@AllArgsConstructor
public class MinIOController {

    private MinIOService minIOService;

    @Log(title = "文件上传", businessType = BusinessType.UPFILE)
    @PostMapping
    @ApiOperation(value = "文件上传", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "文件", paramType = "form", dataType = "__file"),
            @ApiImplicitParam(name = "bucketName", value = "桶名称", paramType = "query", dataType = "string")
    })
    public Result<String> upload(
            @RequestParam(value = "file") MultipartFile file,
            @RequestParam(value = "bucketName", required = false, defaultValue = "default") String bucketName
    ) {
        try {
            String fileType = FileTypeUtils.getFileType(file);
            if (fileType != null) {
            String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1);
            String objectName = IdUtil.simpleUUID() + "." + suffix;
            String path = minIOService.putObject(bucketName, objectName, file);
            return Result.success(path);
            }
            return Result.failed("不支持的文件格式。请确认格式,重新上传!!!");
        } catch (Exception e) {
            throw new BizException(e.getMessage());
        }
    }

    @Log(title = "文件删除", businessType = BusinessType.DELETE)
    @DeleteMapping
    @ApiOperation(value = "文件删除", httpMethod = "DELETE")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "path", value = "文件路径", required = true, paramType = "query"),
    })
    @SneakyThrows
    public Result removeFile(@RequestParam String path) {
        int lastIndex = path.lastIndexOf("/");
        String bucketName = path.substring(path.lastIndexOf("/", lastIndex - 1) + 1, lastIndex);
        String objectName = path.substring(lastIndex + 1);
        minIOService.removeObject(bucketName, objectName);
        return Result.success();
    }

}

- 测试

在这里插入图片描述
完结:之前做文件存储实现的一个minio案例,有其他问题再进行改进,特意再此记录一下
感谢!

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值