spring boot:使用docker安装minio(spring boot使用)

305 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情

使用docker安装minio(spring boot使用)

查看docker环境是否正常

docker search minIO

下载minIO的镜像

docker pull minio/minio

创建minIO容器

查看所有镜像

docker images

创建目录:

mkdir -p /data/minio/config
mkdir -p /data/minio/data

创建并启动minIO容器: 这里的 \ 指的是命令还没有输入完,还需要继续输入命令,先不要执行的意思。 这里的9090端口指的是minio的客户端端口。虽然设置9090,但是我们在访问9000的时候,他会自动跳到9090。

docker run -p 9091:9091 -p 9090:9090 \
 --net=host \
 --name minio \
 -d --restart=always \
 -e "MINIO_ACCESS_KEY=admin" \
 -e "MINIO_SECRET_KEY=Cjw970404" \
 -v /docker/minio/data:/docker/minio/data \
 -v /docker/minio/config:/docker/minio/config \
 minio/minio server \
 /data --console-address ":9090" -address ":9091"


 # 设置为和 docker 绑定启动,docker 启动则 minio 就启动
docker update --restart=always

访问地址

http://ip:9090

到这里。minio就算安装完成了

SpringBoot整合Minio

首先我们先要导入项目依赖

导入依赖

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

application.properties配置信息

这边的配置,需要自己根据需求进行配置。

# 限制上传文件大小
spring.servlet.multipart.max-file-size=200MB
spring.servlet.multipart.max-request-size=200MB

# minio配置
minio.endpoint=http://*.*.*.*:9001
minio.bucketName=taluohui
minio.accessKey=admin
minio.secretKey=Cjw970404

MinioConfig.class配置类

将配置文件中的配置读取出来,并进行MinioClient的配置申明。

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

    @Value("${minio.url}")
    private String endpoint;

    @Value("${minio.accessKey}")
    private String accessKey;

    @Value("${minio.secretKey}")
    private String secretKey;

    @Value("${minio.bucketName}")
    private String bucketName;

    @Bean
    public MinioClient minioClient() {
        return MinioClient.builder()
                .endpoint(endpoint)
                .credentials(accessKey, secretKey)
                .build();
    }
}

minio工具类

实现minio的配置方法,更加方便我们之后的时候。

@Component
@Slf4j
public class MinioUtil {
    @Autowired
    private MinioConfig prop;

    @Resource
    private MinioClient minioClient;

    /**
     * 查看存储bucket是否存在
     * @return boolean
     */
    public Boolean bucketExists(String bucketName) {
        Boolean found;
        try {
            found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return found;
    }

    /**
     * 创建存储bucket
     * @return Boolean
     */
    public Boolean makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
    /**
     * 删除存储bucket
     * @return Boolean
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
    /**
     * 获取全部bucket
     */
    public List<Bucket> getAllBuckets() {
        try {
            List<Bucket> buckets = minioClient.listBuckets();
            return buckets;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }



    /**
     * 文件上传
     *
     * @param file 文件
     * @return Boolean
     */
    public String upload(MultipartFile file) {
        String originalFilename = file.getOriginalFilename();
        if (StringUtils.isBlank(originalFilename)){
            throw new RuntimeException();
        }
        String fileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        String objectName = formatter.format(new Date()) + "/" + fileName;
        try {
            PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(prop.getBucketName()).object(objectName)
                    .stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();
            //文件名称相同会覆盖
            minioClient.putObject(objectArgs);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return objectName;
    }

    /**
     * 预览图片
     * @param fileName
     * @return
     */
    public String preview(String fileName){
        // 查看文件地址
        GetPresignedObjectUrlArgs build = new GetPresignedObjectUrlArgs().builder().bucket(prop.getBucketName()).object(fileName).method(Method.GET).build();
        try {
            String url = minioClient.getPresignedObjectUrl(build);
            return url;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 文件下载
     * @param fileName 文件名称
     * @param res response
     * @return Boolean
     */
    public void download(String fileName, HttpServletResponse res) {
        GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(prop.getBucketName())
                .object(fileName).build();
        try (GetObjectResponse response = minioClient.getObject(objectArgs)){
            byte[] buf = new byte[1024];
            int len;
            try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()){
                while ((len=response.read(buf))!=-1){
                    os.write(buf,0,len);
                }
                os.flush();
                byte[] bytes = os.toByteArray();
                res.setCharacterEncoding("utf-8");
                // 设置强制下载不打开
                // res.setContentType("application/force-download");
                res.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
                try (ServletOutputStream stream = res.getOutputStream()){
                    stream.write(bytes);
                    stream.flush();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 查看文件对象
     * @return 存储bucket内文件对象信息
     */
    public List<Item> listObjects() {
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(prop.getBucketName()).build());
        List<Item> items = new ArrayList<>();
        try {
            for (Result<Item> result : results) {
                items.add(result.get());
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return items;
    }

    /**
     * 删除
     * @param fileName
     * @return
     * @throws Exception
     */
    public boolean remove(String fileName){
        try {
            minioClient.removeObject( RemoveObjectArgs.builder().bucket(prop.getBucketName()).object(fileName).build());
        }catch (Exception e){
            return false;
        }
        return true;
    }
}

文件处理接口

实现文件增删改查等操作的接口。

@Api(tags = "文件相关接口")
@Slf4j
@RestController
@RequestMapping("/product/file")
public class FileController {
    @Autowired
    private MinioUtil minioUtil;
    @Autowired
    private MinioConfig prop;

    @ApiOperation(value = "查看存储bucket是否存在")
    @GetMapping("/bucketExists")
    public JsonResponse bucketExists(@RequestParam("bucketName") @Validated @NotBlank(message = "存储桶名不能为空") String bucketName) {
        if (minioUtil.bucketExists(bucketName)) {
            return JsonResponse.success("存储bucket存在");
        } else {
            return JsonResponse.fail(500, "存储bucket不存在");
        }
    }

    @ApiOperation(value = "创建存储bucket")
    @GetMapping("/makeBucket")
    public JsonResponse makeBucket(@RequestParam("bucketName") @Validated @NotBlank(message = "存储桶名不能为空") String bucketName) {
        if (minioUtil.makeBucket(bucketName)) {
            return JsonResponse.success("存储bucket创建成功");
        } else {
            return JsonResponse.fail(500, "存储bucket创建失败");
        }
    }

    @ApiOperation(value = "删除存储bucket")
    @GetMapping("/removeBucket")
    public JsonResponse removeBucket(String bucketName) {
        if (minioUtil.removeBucket(bucketName)) {
            return JsonResponse.success("存储bucket删除成功");
        } else {
            return JsonResponse.fail(500, "存储bucket删除失败");
        }
    }

    @ApiOperation(value = "获取全部bucket")
    @GetMapping("/getAllBuckets")
    public JsonResponse getAllBuckets() {
        List<Bucket> allBuckets = minioUtil.getAllBuckets();
        return new JsonResponse<>(allBuckets);
    }

    @ApiOperation(value = "文件上传返回url")
    @PostMapping("/upload")
    public JsonResponse upload(@RequestParam("file") MultipartFile file) {
        String objectName = minioUtil.upload(file);
        if (null != objectName) {
            return JsonResponse.success(prop.getEndpoint() + "/" + prop.getBucketName() + "/" + objectName);
        }
        return JsonResponse.fail();
    }

    @ApiOperation(value = "图片/视频预览")
    @GetMapping("/preview")
    public JsonResponse preview(@RequestParam("fileName") String fileName) {
        return JsonResponse.success(minioUtil.preview(fileName));
    }

    @ApiOperation(value = "文件下载")
    @GetMapping("/download")
    public JsonResponse download(@RequestParam("fileName") String fileName, HttpServletResponse res) {
        minioUtil.download(fileName,res);
        return JsonResponse.success();
    }

    @ApiOperation(value = "删除文件", notes = "根据url地址删除文件")
    @PostMapping("/delete")
    public JsonResponse remove(String url) {
        String objName = url.substring(url.lastIndexOf(prop.getBucketName()+"/") + prop.getBucketName().length()+1);
        minioUtil.remove(objName);
        return JsonResponse.success(objName);
    }
}

解决链接7天失效问题

访问minio的管理界面,点击对应的存储桶的设置 1656645400991.jpg Access Policy选项选择custom 1656645936988.jpg 将其中的ListBucket配置删除掉,这样别人就没办法通过存储桶地址获取所有图片,并删除图片了。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "*"
                ]
            },
            "Action": [
                "s3:GetBucketLocation",
                "s3:ListBucketMultipartUploads"
            ],
            "Resource": [
                "arn:aws:s3:::taluohui"
            ]
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "*"
                ]
            },
            "Action": [
                "s3:AbortMultipartUpload",
                "s3:DeleteObject",
                "s3:GetObject",
                "s3:ListMultipartUploadParts",
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::taluohui/*"
            ]
        }
    ]
}