SpringBoot集成MinIO实践

简介: SpringBoot集成MinIO实践

背景


MinIO 是全球领先的对象存储先锋,在标准硬件上,读/写速度上高达183 GB/秒和171 GB/秒。MinIO用作云原生应用程序的主要存储,与传统对象存储相比,云原生应用程序需要更高的吞吐量和更低的延迟。通过添加更多集群可以扩展名称空间,更多机架,直到实现目标。同时,符合一切原生云计算的架构和构建过程,并且包含最新的云计算的全新的技术和概念。


关于对象存储,使用起来无非就是文件上传、下载与删除,再加上桶的操作而已。这里使用 SpringBoot 集成 MinIO 单实例实战,关于 MinIO 分布式集群的高可用性、可扩展性测试,可参考文章:全栈开发之MinIO分布式文件存储集群高可用测试


  1. 桶管理;
  2. 对象管理(上传、下载、删除);
  3. 对象预签名;
  4. 桶策略管理;

image.png


依赖


Note: 我这里使用 7.x 的版本进行实验与演示,最新版的 8.xMinIO 后台管理界面有所不同,但是经过我们实际生产的测试,接口都是兼容的。

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


MinIO在Docker下单实例运行


docker run -p 9000:9000 \
  --name minio1 \
  -v /opt/minio/data-single \
  -e "MINIO_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE" \
  -e "MINIO_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" \
  minio/minio server /data

image.png


接口测试


基于 MinIO 客户端,主要实现了桶管理,对象管理,对象预签名等服务接口。

RESTful API 对外提供文件上传、下载、删除操作接口;


使用 PostMan 测试文件上传、下载、删除接口:头信息: Content-Type:multipart/form-data

# 上传
curl --location --request POST 'localhost:8090/minio/uploadFile' \
--header 'Content-Type: multipart/form-data' \
--form 'file=@"/C:/Users/nxq01/Downloads/springboot-minio-master.zip"'
# 下载
curl --location --request POST 'localhost:8090/minio/downloadFile' \
--form 'bucketName="heartsuit"' \
--form 'originalName="springboot-minio-master.zip"' \
--form 'filePath="2021-11-23/92cf3f69-501b-41de-83ae-f67e5a57f35f.zip"'
# 删除
curl --location --request POST 'localhost:8090/minio/deleteFile' \
--header 'Content-Type: multipart/form-data' \
--form 'bucketName="heartsuit"' \
--form 'filePath="2021-11-23/92cf3f69-501b-41de-83ae-f67e5a57f35f.zip"'


核心接口


  • Controller
@RequestMapping("/minio")
@RestController
@Slf4j
public class MinIOController {
    @Autowired
    private MinIOService minIOService;
    @Autowired
    private MinIOConfig minioConfig;
    @PostMapping("/uploadFile")
    public Result<String> uploadFile(MultipartFile file, String bucketName) {
        try {
            bucketName = !StringUtils.isEmpty(bucketName) ? bucketName : minioConfig.getBucketName();
            if (!minIOService.bucketExists(bucketName)) {
                minIOService.makeBucket(bucketName);
            }
            String fileName = file.getOriginalFilename();
            assert fileName != null;
            // 根据业务设计,设置存储路径:按天创建目录
            String objectName = new SimpleDateFormat("yyyy-MM-dd/").format(new Date())
                    + UUID.randomUUID().toString()
                    + fileName.substring(fileName.lastIndexOf("."));
            minIOService.putObject(bucketName, file, objectName);
            log.info("文件格式为:{}", file.getContentType());
            log.info("文件原名称为:{}", fileName);
            log.info("文件存储的桶为:{}", bucketName);
            log.info("文件对象路径为:{}", objectName);
            return Result.success(minIOService.getObjectUrl(bucketName, objectName));
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error("上传失败");
        }
    }
    @PostMapping("/deleteFile")
    public Result<String> deleteFile(String bucketName, String filePath) throws Exception {
        if (!minIOService.bucketExists(bucketName)) {
            throw new Exception("不存在该桶");
        }
        boolean status = minIOService.removeObject(bucketName, filePath);
        return status ? Result.success("删除成功") : Result.success("删除失败");
    }
    @PostMapping("/downloadFile")
    public Result<String> downloadFile(String bucketName, String filePath, String originalName, HttpServletResponse response) throws Exception {
        if (!minIOService.bucketExists(bucketName)) {
            throw new Exception("不存在该桶");
        }
        boolean status = minIOService.downloadFile(bucketName, filePath, originalName, response);
        return status ? Result.success("下载成功") : Result.success("下载失败");
    }
}
  • Service
@Service
public class MinIOServiceImpl  implements MinIOService {
    @Autowired
    private MinIOUtils minIOUtils;
    /**
     * 判断 bucket是否存在
     *
     * @param bucketName
     * @return
     */
    @Override
    public boolean bucketExists(String bucketName) {
        return minIOUtils.bucketExists(bucketName);
    }
    /**
     * 创建 bucket
     *
     * @param bucketName
     */
    @Override
    public void makeBucket(String bucketName) {
        minIOUtils.makeBucket(bucketName);
    }
    /**
     * 文件上传
     *
     * @param bucketName
     * @param objectName
     * @param filename
     */
    @Override
    public void putObject(String bucketName, String objectName, String filename) {
        minIOUtils.putObject(bucketName, objectName, filename);
    }
    @Override
    public void putObject(String bucketName, String objectName, InputStream stream, String contentType) {
        minIOUtils.putObject(bucketName, objectName, stream, contentType);
    }
    /**
     * 文件上传
     *
     * @param bucketName
     * @param multipartFile
     */
    @Override
    public void putObject(String bucketName, MultipartFile multipartFile, String filename) {
        minIOUtils.putObject(bucketName, multipartFile, filename);
    }
    /**
     * 删除文件
     * @param bucketName
     * @param filePath
     */
    @Override
    public boolean removeObject(String bucketName,String filePath) {
        return minIOUtils.removeObject(bucketName,filePath);
    }
    /**
     * 下载文件
     * @param bucketName
     * @param filePath
     * @param originalName
     * @param response
     * @return
     */
    @Override
    public boolean downloadFile(String bucketName, String filePath, String originalName, HttpServletResponse response) {
        return minIOUtils.downloadFile(bucketName,filePath, originalName, response);
    }
    /**
     * 获取文件路径
     * @param bucketName
     * @param objectName
     * @return
     */
    @Override
    public String getObjectUrl(String bucketName,String objectName) {
        return minIOUtils.getObjectUrl(bucketName,objectName);
    }
}

minIOUtils 工具类省略,可参考文末的项目源码。


可能遇到的问题


  • SpringBoot上传文件报错:org.springframework.web.multipart. MaxUploadSizeExceededException: Maximum upload size exceeded; nested exception is java.lang. IllegalStateException: org.apache.tomcat.util.http.fileupload.impl. FileSizeLimitExceededException: The field file exceeds its maximum permitted size of 1048576 bytes.


这是因为 SpringBoot 项目限制了上传文件的大小,默认为1M,当用户上传了超过1M大小的文件时,就会抛出上述错误,可通过以下配置修改。


spring:
  servlet:
    multipart:
      maxFileSize: 10MB
      maxRequestSize: 30MB
  • 通过docker exec -it cd34c345960c /bin/bash无法进入容器,报错:OCI runtime exec failed: exec failed: container_linux.go:349: starting container process caused "exec: "/": permission denied": unknown


解决: docker exec -it cd34c345960c /bin/bash 改为: docker exec -it cd34c345960c sh 或者: docker exec -it cd34c345960c /bin/sh


目录
相关文章
|
4天前
|
存储 前端开发 Java
Spring Boot 集成 MinIO 与 KKFile 实现文件预览功能
本文详细介绍如何在Spring Boot项目中集成MinIO对象存储系统与KKFileView文件预览工具,实现文件上传及在线预览功能。首先搭建MinIO服务器,并在Spring Boot中配置MinIO SDK进行文件管理;接着通过KKFileView提供文件预览服务,最终实现文档管理系统的高效文件处理能力。
|
1天前
|
Devops jenkins 测试技术
DevOps实践:持续集成与持续部署(CI/CD)的实现之路
【9月更文挑战第33天】在软件开发的海洋中,DevOps是一艘能够加速航行、提升航程质量的巨轮。本文将作为你的航海图,指引你理解并实现DevOps文化中的核心环节——持续集成(CI)与持续部署(CD)。我们将从基础概念出发,逐步深入到实际操作,带你领略代码到部署的全过程。准备好扬帆起航,让我们共同探索如何通过自动化工具和流程优化,让软件交付变得既高效又可靠。
|
4天前
|
监控 Devops 测试技术
DevOps实践:持续集成与部署的自动化之路
【9月更文挑战第30天】在软件工程的世界中,DevOps已成为提升开发效率、确保软件质量和加快交付速度的关键策略。本文将深入探讨如何通过自动化工具和流程实现持续集成(CI)与持续部署(CD),从而优化软件开发周期。我们将从基础概念出发,逐步深入到实际操作,最终展示如何构建一个高效的自动化流水线,以支持快速迭代和高质量发布。
22 7
|
14天前
|
Devops jenkins Java
DevOps实践:持续集成和部署的自动化之旅
【9月更文挑战第20天】在软件开发的世界里,速度和质量是至关重要的。本文将带领读者踏上一场自动化之旅,深入探索DevOps文化中的两大支柱——持续集成(CI)和持续部署(CD)。我们将通过一个实际的案例,展示如何利用现代工具和技术实现代码从编写到部署的无缝转换,确保软件交付的高效性和可靠性。准备好让你的开发流程变得更加流畅和高效了吗?让我们开始吧!
|
5天前
|
Devops jenkins 测试技术
DevOps实践:持续集成与自动化测试的融合之道
【9月更文挑战第29天】在软件开发的快节奏竞赛中,DevOps如同一位智慧的舵手,引领着船只驶向效率与质量的彼岸。本文将揭开DevOps的神秘面纱,探索其核心理念如何通过持续集成(CI)和自动化测试的实践,实现软件开发流程的优化与加速。我们将一同见证代码从构思到部署的旅程,以及这一过程中的关键技术和工具如何协同工作,确保软件质量和交付速度的双重提升。
|
8天前
|
XML Java 关系型数据库
springboot 集成 mybatis-plus 代码生成器
本文介绍了如何在Spring Boot项目中集成MyBatis-Plus代码生成器,包括导入相关依赖坐标、配置快速代码生成器以及自定义代码生成器模板的步骤和代码示例,旨在提高开发效率,快速生成Entity、Mapper、Mapper XML、Service、Controller等代码。
springboot 集成 mybatis-plus 代码生成器
|
8天前
|
Java Spring
springboot 集成 swagger 2.x 和 3.0 以及 Failed to start bean ‘documentationPluginsBootstrapper‘问题的解决
本文介绍了如何在Spring Boot项目中集成Swagger 2.x和3.0版本,并提供了解决Swagger在Spring Boot中启动失败问题“Failed to start bean ‘documentationPluginsBootstrapper’; nested exception is java.lang.NullPointerEx”的方法,包括配置yml文件和Spring Boot版本的降级。
springboot 集成 swagger 2.x 和 3.0 以及 Failed to start bean ‘documentationPluginsBootstrapper‘问题的解决
|
19天前
|
Ubuntu jenkins 测试技术
软件测试中的自动化与持续集成实践
【9月更文挑战第15天】在软件开发的快节奏世界中,自动化测试和持续集成(CI)已成为确保质量和效率的关键策略。本文旨在揭示如何通过实施自动化测试框架和CI流程来优化开发周期,减少人为错误,并加快产品上市时间。我们将探讨一些实用的工具和技术,以及它们如何帮助团队实现更流畅、更可靠的软件发布。
|
24天前
|
监控 Devops 测试技术
DevOps实践: 持续集成和持续部署(CI/CD)的入门指南
【9月更文挑战第10天】在快速迭代的软件开发世界中,DevOps已经成为加速产品交付、提升软件质量和团队协作的关键策略。本文将深入浅出地介绍DevOps的核心组成部分——持续集成(Continuous Integration, CI)与持续部署(Continuous Deployment, CD)的基本概念、实施步骤以及它们如何革新传统的软件开发流程。你将学习到如何通过自动化工具简化开发流程,并理解为什么CI/CD是现代软件开发不可或缺的一环。
|
1天前
|
运维 Devops 测试技术
DevOps实践之路:从持续集成到自动化部署
【9月更文挑战第33天】在软件开发的海洋中,DevOps如同一艘航船,承载着敏捷开发与运维之间的桥梁。本文将带你领略DevOps的魅力,从持续集成的理念出发,穿越自动化测试的浪潮,直至自动化部署的港湾。我们将通过实际案例,探索如何构建一个高效、可靠的DevOps流程,让软件交付不再是梦魇,而是流畅的艺术。
下一篇
无影云桌面