原创

Spring Boot 与 MinIO 的结合:高效存储解决方案


一、Spring Boot 与 MinIO 简介

Spring Boot 简化了基于 Spring 的应用开发,它通过自动配置和约定优于配置的原则,让开发者能够快速构建独立的、生产级别的应用。

MinIO 是一个开源的对象存储服务器,支持与 Amazon S3 兼容的 API,具有高性能、可扩展性和易用性等特点。它适用于存储各种类型的非结构化数据,如图像、视频、文档等。

二、为什么要将 Spring Boot 与 MinIO 结合?

1. 灵活的存储选择

MinIO 提供了灵活的存储选项,可以在本地、云端或混合环境中部署,满足不同应用场景的需求。

例如,如果您的应用处于开发阶段,可以选择在本地部署 MinIO 进行测试和开发;而在生产环境中,可以将其部署到云端,以获得更好的扩展性和可靠性。

2. 简化的开发过程

Spring Boot 的强大生态和丰富的库使得与 MinIO 的集成变得非常简单。通过使用相关的依赖和配置,我们可以快速在应用中实现文件的上传、下载和管理功能。

3. 高性能和可扩展性

MinIO 本身具有出色的性能和可扩展性,可以轻松应对大量的数据存储和访问请求。结合 Spring Boot 的优化和并发处理能力,能够为应用提供高效的存储服务。

三、如何在 Spring Boot 中集成 MinIO

1. 添加依赖

pom.xml 文件中添加相关的依赖,例如 minio-java 库。

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
		<exclusions>
			<exclusion>
				<groupId>ch.qos.logback</groupId>
				<artifactId>logback-classic</artifactId>
			</exclusion>
			<exclusion>
				<groupId>ch.qos.logback</groupId>
				<artifactId>logback-core</artifactId>
			</exclusion>
		</exclusions>
	</dependency>
	<dependency>
    	<groupId>io.minio</groupId>
    	<artifactId>minio</artifactId>
    	<version>8.4.1</version>
	</dependency>
	<dependency>
		<groupId>org.projectlombok</groupId>
		<artifactId>lombok</artifactId>
	</dependency>
</dependencies>

2. 配置 MinIO 连接信息

application.propertiesapplication.yml 中配置 MinIO 的服务器地址、访问密钥和秘密密钥等信息。

minio.endpoint=http://127.0.0.1:9000
minio.accessKey=your-access-key
minio.secretKey=your-secret-key
minio.bucket-name=test

3. 编写代码实现文件操作

创建配置类

import io.minio.MinioClient;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
@ConfigurationProperties(prefix = "minio")
public class MinioConfig {
    private String accessKey;
    private String secretKey;
    private String url;
    private String bucketName;

    @Bean
    public MinioClient minioClient(){
        return MinioClient.builder()
                .region("cn-north-1")
                .endpoint(url)
                .credentials(accessKey, secretKey)
                .build();
    }
}

创建操作工具

import com.like0.springbootminio.config.MinioConfig;
import io.minio.*;
import io.minio.http.Method;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.concurrent.TimeUnit;
 
@Component
public class MinioUtil {
 
    @Resource
    private MinioClient minioClient;

    @Resource
    private MinioConfig configuration;
 
    /**
     * 判断bucket是否存在,不存在则创建
     */
    public void existBucket(String bucketName) throws Exception {
        boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (!exists) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }
 
    /**
     * 删除bucket
     */
    public Boolean removeBucket(String bucketName) throws Exception {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            throw e;
        }
        return true;
    }
 
    /**
     * 上传文件
     *
     * @param file     文件
     * @param fileName 文件名称
     */
    public void upload(MultipartFile file, String fileName) throws Exception {
        // 使用putObject上传一个文件到存储桶中。
        InputStream is = file.getInputStream();
        minioClient.putObject(PutObjectArgs.builder()
                .bucket(configuration.getBucketName())
                .object(fileName)
                .stream(is, file.getSize(), -1)
                .contentType(file.getContentType())
                .build());
    }
 
    /**
     * 获取文件访问地址(有过期时间)
     *
     * @param fileName 文件名称
     * @param time     时间
     * @param timeUnit 时间单位
     */
    public String getExpireFileUrl(String fileName, int time, TimeUnit timeUnit) throws Exception {
        return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                .method(Method.GET)
                .bucket(configuration.getBucketName())
                .object(fileName)
                .expiry(time, timeUnit).build());
    }
 
    /**
     * 获取文件访问地址
     *
     * @param fileName 文件名称
     */
    public String getFileUrl(String fileName) throws Exception {
        return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                .method(Method.GET)
                .bucket(configuration.getBucketName())
                .object(fileName)
                .build()
        );
}
 
    /**
     * 下载文件
     *
     * @param fileName 文件名称
     */
    public void download(HttpServletResponse response, String fileName) throws Exception {
        InputStream in = null;
        try {
            // 获取对象信息
            StatObjectResponse sos = minioClient.statObject(StatObjectArgs.builder().bucket(configuration.getBucketName()).object(fileName).build());
            response.setContentType(sos.contentType());
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            // 文件下载
            in = minioClient.getObject(GetObjectArgs.builder().bucket(configuration.getBucketName()).object(fileName).build());
            IOUtils.copy(in, response.getOutputStream());
        } catch (Exception e) {
            throw e;
        } finally {
            if (in != null) {
                in.close();
            }
        }
    }
 
    /**
     * 删除文件
     *
     * @param fileName 文件名称
     */
    public void delete(String fileName) throws Exception {
        minioClient.removeObject(RemoveObjectArgs.builder().bucket(configuration.getBucketName()).object(fileName).build());
    }
}

使用示例

import com.like0.springbootminio.utils.MinioUtil;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;

@RestController("uploadController")
@RequestMapping("/api")
public class UploadController {

    @Resource
    private MinioUtil minioUtil;

    @PostMapping("/upload")
    public String upload(MultipartFile[] files) throws Exception {
        String rlt = "";
        for (MultipartFile file : files){
            String fileName = file.getOriginalFilename();
            minioUtil.upload(file, fileName);
            rlt += fileName + " , ";
        }
        return "上传成功:" + rlt;

    }

    /**
     * 预览文件
     */
    @GetMapping("/preview")
    public String preview(String fileName) throws Exception {
        return minioUtil.getFileUrl(fileName);
    }

    /**
     * 下载文件
     */
    @GetMapping("/download")
    public void download(String fileName, HttpServletResponse response) throws Exception {
        minioUtil.download(response, fileName);
    }

    /**
     * 删除文件
     */
    @PostMapping("/delete")
    public String delete(String fileName) throws Exception {
        minioUtil.delete(fileName);
        return "删除成功";
    }

}

四、实际应用场景举例

1. 图片存储与展示

在一个图片分享应用中,用户上传的图片可以存储在 MinIO 中。当其他用户浏览时,从 MinIO 中快速获取并展示图片。

2. 文档管理系统

企业的文档管理系统可以使用 MinIO 来存储各类文档,通过 Spring Boot 应用实现文档的上传、下载、分类和检索等功能。

3. 视频流媒体服务

对于视频流媒体平台,将视频文件存储在 MinIO 中,结合 Spring Boot 实现视频的播放控制和权限管理。

五、总结

Spring Boot 与 MinIO 的结合为开发者提供了一种强大而灵活的存储解决方案。通过简单的配置和代码实现,我们能够轻松构建高效、可靠的数据存储和管理功能,为应用的发展提供坚实的基础。希望在项目中成功运用这一组合,提升应用的性能和用户体验。

SpringBoot
  • 作者:一介闲人(联系作者)
  • 发表时间: 2024-08-08 18:01
  • 版权声明:原创-转载需保持署名
  • 公众号转载:请在文末添加本文链接
  • 评论