SpringBoot后端开发常用工具详细介绍——Minio资源管理器

背景

在后端开发中,常常会涉及到各种静态资源的操作,比如上传图片、下载图片、上传下载PDF

那么我们在进行迁移开发和部署或者各种分布式开发时就会面对着这些资源的处理,以防止目录丢失或者其他异常。

Minio就是一个对象存储解决方案,一般都做min-io,顾名思义就是:最小输入输出

MinIO有能力在任何地方部署 - 公有云或私有云,裸金属基础设施,编排环境,以及边缘基础设施。所以可以更快速的解决上面提到的一系列问题。

操作

这里以我们常用的windows开发环境为例,开发环境熟练使用后,部署时也能很顺畅的使用了。

先简单说一下minio里的一个概念

bucket桶: 可以简单理解为文件夹,桶是 MinIO 中用于组织和管理对象的容器。每个桶都有一个唯一的名称,在同一个 MinIO 实例中,桶名称必须是唯一的。用户可以通过桶来对对象进行逻辑分组。

1.下载并启动minio

下载地址为:https://dl.minio.org.cn/server/minio/release/windows-amd64/minio.exe

启动命令:在minio.exe文件目录下打开cmd并执行minio.exe server minio,将会启动minio,并且打印访问的url和用户名、密码

访问minio的url地址,并输入上面的用户名密码:

2. 添加依赖与配置项

pom.xml

xml 复制代码
<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>8.3.6</version>
</dependency>

application.yml

yaml 复制代码
spring:
  minio:
    access-key: minioadmin # 用户名
    secret-key: minioadmin #密码
    url: http://127.0.0.1:9000  #访问地址

3. 创建MinioConfig配置类信息

通过以下的配置,我们就可以创建一个适用于我们上面启动的minio的配置项,同时完成了MinioClient的注册

java 复制代码
@Data
@Configuration
@ConfigurationProperties(prefix = "spring.minio")
public class MinioConfig {

    private String accessKey;
    private String secretKey;
    private String url;
	// 或者,主要目的是获取yml中的配置内容
    //@Value("${spring.minio.url}")
    //private String minioEndpoint;
    //@Value("${spring.minio.access-key}")
    //private String minioAccessKey;
    //@Value("${spring.minio.secret-key}")
    //private String minioSecretKey;
    @Bean
    public MinioClient minioClient(){
        return MinioClient.builder()
                .endpoint(url)
                .credentials(accessKey,secretKey)
                .build();
    }
}

4. 常用操作

我们可以进入自动注入的minioClient配置类查看常用操作

我们主要介绍常用的一些操作

4.0 创建单元测试

创建单元测试,并自动注入MinioClient

java 复制代码
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class test {
    @Resource
    private MinioClient minioClient;
   	// 以桶为test,文件为D:/test/test.jpg为案例
    private final String bucketName = "test";
    File file = new File("D:/test/test.jpg");
}
4.1 创建桶
java 复制代码
@Test
void createBucket() throws Exception {
    if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
        minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
    }
}

可以看到已经成功创建了桶

4.2 删除桶
java 复制代码
@Test
void deleteBucket() throws Exception {
    if (minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
        minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
    }
}

可以看到桶被删除了

4.3 检测桶是否存在
java 复制代码
@Test
void bucketExists() throws Exception {
    boolean exists = minioClient.bucketExists(
            BucketExistsArgs.builder().bucket(bucketName).build());
    System.out.println(exists);
}

当我们分别检测桶发现桶不存在

重新执行创建桶操作,可以看到桶返回存在

4.4 上传文件
java 复制代码
@Test
void fileUpload() throws Exception {
    FileInputStream inputStream = new FileInputStream(file);
    minioClient.putObject(PutObjectArgs.builder()
            .bucket(bucketName)
            .object(file.getName())
            .stream(inputStream, file.length(), -1)
            .contentType(MediaType.IMAGE_JPEG_VALUE)
            .build());
}

test桶中添加了新的文件

4.5 下载文件
java 复制代码
@Test
void fileDownload() throws Exception {
    try (InputStream stream = minioClient.getObject(
            GetObjectArgs.builder()
                    .bucket(bucketName)
                    .object(file.getName())
                    .build())) {
        // Save the file locally or process the stream as needed
        Files.copy(stream, Paths.get("D:/test/downloaded_" + file.getName()));
    }
}

文件按照路径和命名存储到了新的位置

4.6 检测文件是否存在
java 复制代码
@Test
void fileExists() throws Exception {
    try {
        StatObjectResponse response = minioClient.statObject(
            StatObjectArgs.builder()
            .bucket(bucketName)
            .object(file.getName())
            .build());
        System.out.println(response);
    } catch (MinioException e) {
        System.out.println(e.getMessage());
    }
}

如果存在,则文件信息被返回

如果不存在,则产生异常,我们可以捕获异常进行处理

4.7 删除文件
java 复制代码
@Test
void fileDelete() throws Exception {
    minioClient.removeObject(
            RemoveObjectArgs.builder()
                    .bucket(bucketName)
                    .object(file.getName())
                    .build());
}

文件被删除了

5. 自定义我们自己的Minio工具类

java 复制代码
@Component
public class MinioUtils {

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private MinioConfig configuration;

    /**
     * @param name 名字
     * @Description description: 判断bucket是否存在,不存在则创建
     */
    public boolean existBucket(String name) {
        boolean exists;
        try {
            exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build());
            if (!exists) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());
                exists = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            exists = false;
        }
        return exists;
    }

    /**
     * @param bucketName 存储bucket名称
     * @Description 创建存储bucket
     */
    public Boolean makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * @param bucketName 存储bucket名称
     * @Description 删除存储bucket
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * @param fileName 文件名称
     * @param time     时间
     * @Description 获取上传临时签名
     */
    @SneakyThrows
    public Map getPolicy(String fileName, ZonedDateTime time,String bucketName) {
        PostPolicy postPolicy = new PostPolicy(bucketName, time);
        postPolicy.addEqualsCondition("key", fileName);
        try {
            Map<String, String> map = minioClient.getPresignedPostFormData(postPolicy);
            HashMap<String, String> map1 = new HashMap<>();
            map.forEach((k, v) -> {
                map1.put(k.replaceAll("-", ""), v);
            });
            map1.put("host", configuration.getUrl() + "/" + bucketName);
            return map1;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @param objectName 对象名称
     * @param method     方法
     * @param time       时间
     * @param timeUnit   时间单位
     * @Description 获取上传文件的url
     */
    public String getPolicyUrl(String objectName, Method method,String bucketName, int time, TimeUnit timeUnit) {
        try {
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(method)
                    .bucket(bucketName)
                    .object(objectName)
                    .expiry(time, timeUnit).build());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * @param file     文件
     * @param fileName 文件名称
     * @Description 上传文件
     */
    public void upload(MultipartFile file, String fileName,String bucketName) {
        // is deviceImg bucket exist ?
        boolean b = existBucket(bucketName);
        if (!b) {
            makeBucket(bucketName);
        }
        // 使用putObject上传一个文件到存储桶中。
        try {
            InputStream inputStream = file.getInputStream();
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(fileName)
                    .stream(inputStream, file.getSize(), -1)
                    .contentType(file.getContentType())
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @param objectName 对象名称
     * @param time       时间
     * @param timeUnit   时间单位
     * @Description 根据filename获取文件访问地址
     */
    public String getUrl(String objectName,String bucketName, int time, TimeUnit timeUnit) {
        String url = null;
        try {
            url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(bucketName)
                    .object(objectName)
                    .expiry(time, timeUnit).build());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return url;
    }

    /**
     * @Description description: 下载文件
     */
    public ResponseEntity<byte[]> download(String fileName,String bucketName) {
        ResponseEntity<byte[]> responseEntity = null;
        InputStream in = null;
        ByteArrayOutputStream out = null;
        try {
            in = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
            out = new ByteArrayOutputStream();
            IOUtils.copy(in, out);
            //封装返回值
            byte[] bytes = out.toByteArray();
            HttpHeaders headers = new HttpHeaders();
            try {
                headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            headers.setContentLength(bytes.length);
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setAccessControlExposeHeaders(Arrays.asList("*"));
            responseEntity = new ResponseEntity<byte[]>(bytes, headers, ResultCode.SUCCESS.code);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return responseEntity;
    }

    /**
     * @param objectFile 对象文件
     */
    public String getFileUrl(String objectFile,String bucketName) {
        try {

            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(bucketName)
                    .object(objectFile)
                    .build()
            );
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }
}

public String getFileUrl(String objectFile,String bucketName) {

try {

        return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                .method(Method.GET)
                .bucket(bucketName)
                .object(objectFile)
                .build()
        );
    } catch (Exception e) {
        e.printStackTrace();
    }

    return null;
}

}

复制代码
相关推荐
KingDol_MIni17 分钟前
Spring Boot 集成 T-io 实现客户端服务器通信
java·服务器·spring boot
许苑向上21 分钟前
Java八股文(下)
java·开发语言
后端码匠22 分钟前
Spring Boot3+Vue2极速整合:10分钟搭建DeepSeek AI对话系统
人工智能·spring boot·后端
逸Y 仙X26 分钟前
Git常见命令--助力开发
java·大数据·git·java-ee·github·idea
独孤求败Ace30 分钟前
第44天:Web开发-JavaEE应用&反射机制&类加载器&利用链&成员变量&构造方法&抽象方法
java·开发语言
FLZJ_KL30 分钟前
【设计模式】【创建型模式】单例模式(Singleton)
java·单例模式·设计模式
CL_IN37 分钟前
企业数据集成:实现高效调拨出库自动化
java·前端·自动化
可乐张40 分钟前
AutoGen 技术博客系列 (九):从 v0.2 到 v0.4 的迁移指南
后端·llm
计算机-秋大田43 分钟前
基于Spring Boot的农产品智慧物流系统设计与实现(LW+源码+讲解)
java·开发语言·spring boot·后端·spring·课程设计
计算机毕设指导61 小时前
基于SpringBoot的城乡商城协作系统【附源码】
java·spring boot·后端·mysql·spring·tomcat·maven