SpringBoot 对接 MinIO 实现文件上传下载删除

news2024/11/20 0:24:47

前言

MinIO 是一个开源的对象存储服务器,它可以存储大容量非结构化的数据,例如图片、音频、视频、日志文件、备份数据和容器/虚拟机镜像等。

Spring Boot 与 MinIO 的整合可以方便地实现文件的上传和下载等功能

在实际应用中,Spring Boot 对接 MinIO 可以用于以下场景:

  • 文件存储:将文件存储在 MinIO 中,以便在需要时进行访问和下载。
  • 数据备份:将数据备份到 MinIO 中,以便在需要时进行恢复。
  • 容器镜像存储:将 Docker 容器镜像存储在 MinIO 中,以便在需要时进行访问和下载。
  • 静态资源存储:将静态资源(如 CSS、JavaScript 和图像)存储在 MinIO 中,以便在需要时进行访问和下载。

通过 Spring Boot 对接 MinIO,你可以方便地管理和操作文件存储,并且可以根据自己的需求进行扩展和定制化

MinIO 安装

MinIO 安装参考:Linux 部署 MinIO 分布式对象存储 & 配置为 typora 图床

引入依赖

maven 依赖

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

配置

配置文件

在 application.yml 中添加

minio:
  endpoint: http://192.168.101.3:9001
  access-key: DHgpKILqY5iNa88LByHx  # minio 的 accessKey
  secret-key: Ppa9Nxes7jOkbBD2BTuFhjFcfGjw3lXmXUItKcBV  # minio 的 secretKey

创建配置类

对应配置文件

@Data
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioInfo {
 
    private String endpoint;
 
    private String accessKey;
 
    private String secretKey;
}

配置客户端

配置 minio 操作客户端

@Configuration
@EnableConfigurationProperties(MinioInfo.class)
public class MinioConfig {
 
    @Autowired
    private MinioInfo minioInfo;
 
    /**
     * 获取 minioClient
     */
    @Bean
    public MinioClient minioClient() throws NoSuchAlgorithmException, KeyManagementException {
        return MinioClient.builder().endpoint(minioInfo.getEndpoint())
                .credentials(minioInfo.getAccessKey(),minioInfo.getSecretKey())
                .build();
   }
}

工具类封装

@Slf4j
@Component
public class MinioUtils {

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private MinioInfo minioInfo;


    /**
     * 上传文件
     *
     * @param file       文件信息
     * @param bucketName minio bucket 名称
     * @return 上传文件路径
     */
    public String uploadFile(MultipartFile file, String bucketName) {
        if (file == null || file.getSize() == 0) {
            log.error("minio 上传文件:{}", "上传文件不能为空");
            return null;
        }
        try {
            // 判断是否存在
            createBucket(bucketName);
            // 原文件名
            String originalFilename = file.getOriginalFilename();
            minioClient.putObject(
                    PutObjectArgs.builder().bucket(bucketName).object(originalFilename).stream(
                            file.getInputStream(), file.getSize(), -1)
                            .contentType(file.getContentType())
                            .build());
            return minioInfo.getEndpoint() + "/" + bucketName + "/" + originalFilename;
        } catch (Exception e) {
            log.error("minio 上传失败:{}", e.getMessage());
        }
        log.error("minio 上传文件:{}", "上传失败");
        return null;
    }

    /**
     * 通过字节流上传
     *
     * @param imageFullPath 图片路径
     * @param bucketName    minio bucket 名称
     * @param imageData     图片数据
     * @return 上传文件路径
     */
    public String uploadImage(String imageFullPath,
                              String bucketName,
                              byte[] imageData) {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(imageData);
        try {
            //判断是否存在
            createBucket(bucketName);
            minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(imageFullPath)
                    .stream(byteArrayInputStream, byteArrayInputStream.available(), -1)
                    .contentType(".jpg")
                    .build());
            return minioInfo.getEndpoint() + "/" + bucketName + "/" + imageFullPath;
        } catch (Exception e) {
            log.error("minio 上传失败:{}", e.getMessage());
        }
        log.error("minio 上传失败:{}", "上传失败");
        return null;
    }

    /**
     * 删除文件
     *
     * @param bucketName minio bucket 名称
     * @param fileName 文件名
     * @return
     */
    public Boolean removeFile(String bucketName, String fileName) {
        try {
            //判断桶是否存在
            boolean res = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (res) {
                //删除文件
                minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName)
                        .object(fileName).build());
            }
        } catch (Exception e) {
            log.error("minio 删除文件失败");
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 下载文件
     *
     * @param fileName 文件名
     * @param bucketName minio bucket 名称
     * @param response 请求响应
     */
    public void fileDownload(String fileName,
                             String bucketName,
                             HttpServletResponse response) {

        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            if (StringUtils.isBlank(fileName)) {
                response.setHeader("Content-type", "text/html;charset=UTF-8");
                String data = "文件下载失败";
                OutputStream ps = response.getOutputStream();
                ps.write(data.getBytes("UTF-8"));
                return;
            }

            outputStream = response.getOutputStream();
            // 获取文件对象
            inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
            byte buf[] = new byte[1024];
            int length = 0;
            response.reset();
            response.setHeader("Content-Disposition", "attachment;filename=" +
                    URLEncoder.encode(fileName.substring(fileName.lastIndexOf("/") + 1), "UTF-8"));
            response.setContentType("application/octet-stream");
            response.setCharacterEncoding("UTF-8");
            // 输出文件
            while ((length = inputStream.read(buf)) > 0) {
                outputStream.write(buf, 0, length);
            }
            System.out.println("下载成功");
            inputStream.close();
        } catch (Throwable ex) {
            response.setHeader("Content-type", "text/html;charset=UTF-8");
            String data = "文件下载失败";
            try {
                OutputStream ps = response.getOutputStream();
                ps.write(data.getBytes("UTF-8"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        } finally {
            try {
                outputStream.close();
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @SneakyThrows
    public void createBucket(String bucketName) {
        // 不存在就创建
        if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }
}

使用

上传文件

@RestController
@RequestMapping("/minio")
public class TestMinioController {

    @Resource
    private MinioUtils minioUtils;

    /**
     * 上传文件
     * @param file 文件
     * @return 路径
     */
    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @ResponseBody
    public BaseResponse<String> uploadByMinio(@RequestParam(name = "file") MultipartFile file) {
        //返回存储路径
        String path = minioUtils.uploadFile(file, "minio-img");
        return ResultUtils.success(path);
    }
}

请求参数为 file,类型为 file,响应回来的 data 为文件路径

image-20231009150510183

请求成功后,可以在 minio 中查看

image-20231009150620190

下载文件

根据文件名下载文件

/**
 * 根据文件名下载文件
 *
 * @param fileName 文件名
 * @param response 请求响应体
 */
@GetMapping("/download")
public void downloadByMinio(@RequestParam(name = "fileName") String fileName,
                            HttpServletResponse response) {
    try {
        minioUtils.fileDownload(fileName, "minio-img", response);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

请求参数为 fileName

image-20231009151358994

删除文件

/**
 * 根据文件名删除文件
 *
 * @param fileName 文件名
 * @return 是否成功
 */
@PostMapping("/delete")
@ResponseBody
public BaseResponse<String> deleteByMonio(String fileName) {
    Boolean result = minioUtils.removeFile("minio-img", fileName);
    if (!result) {
        return ResultUtils.error(ErrorCode.SYSTEM_ERROR,"删除失败");
    }
    return ResultUtils.success("删除成功");
}

image-20231009151511984

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1075963.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C# 人像卡通化 Onnx photo2cartoon

效果 项目 代码 using Microsoft.ML.OnnxRuntime; using Microsoft.ML.OnnxRuntime.Tensors; using OpenCvSharp; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms;nam…

NLP项目:维基百科文章爬虫和分类【02】 - 语料库转换管道

一、说明 我的NLP项目在维基百科条目上下载、处理和应用机器学习算法。相关上一篇文章中&#xff0c;展示了项目大纲&#xff0c;并建立了它的基础。首先&#xff0c;一个 Wikipedia 爬网程序对象&#xff0c;它按名称搜索文章&#xff0c;提取标题、类别、内容和相关页面&…

【毕设选题】深度学习 机器视觉 车位识别车道线检测 - python opencv

0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要求&#xff0c;这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。 为了大家能够顺利以及最少的精力通过…

第二证券:如何选股票的龙头股?

在股票商场中&#xff0c;每个出资者的方针都是可以出资到那些未来可以表现出色并带领整个工作开展的龙头股。选股关于出资者来说非常要害&#xff0c;由于选股不妥或许会导致出资失利。那么&#xff0c;怎么选股票的龙头股呢&#xff1f;本文从多个角度进行剖析&#xff0c;协…

platformIO开发arduino

第一先安装arduino,再在arduino库里面安装第三方库。然后下载vscode,在vscode上安装platformIO&#xff0c;然后点击Quick Access下的Import Arduino Project 然后选择自己的arudino项目&#xff0c;一般在用户的Document下面 进入带有.ino后缀的文件夹里然后点击import就可以将…

C语言每日一题(10) 回形矩阵

题目链接 分析思路 我采用的设计思路是从外围开始向里面赋值&#xff0c;关键在于循环的判断条件&#xff0c;从外围的上下左右行依次赋值&#xff0c;然后再向里继续。 1.取得中心值的方法是&#xff1a;用n/2再向上取整&#xff0c;注意类型的转换&#xff0c;因为如果是整…

软件工程与计算总结(六)需求分析方法

本贴介绍需求分析方法&#xff0c;涉及到诸多实践性的东西&#xff0c;掌握各种图表的绘制是重中之重~ 一.需求分析基础 1.原因 需求获取中得到的信息仅仅解释了用户对软件系统的理解与期待&#xff0c;使用的是实际业务的表达方式&#xff0c;还不是开发者能够立即加以实现…

Ubuntu20.04安装Ipopt的流程介绍及报错解决方法(亲测简单有效)

本文主要介绍在Ubuntu20.04中安装Ipopt库的流程&#xff0c;及过程报错的解决方法&#xff0c;已经有很多关于Ipopt安装的博客&#xff0c;但经过我的测试&#xff0c;很多都失效了&#xff0c;因此&#xff0c;经过探索&#xff0c;我找到可流畅的安装Ipopt的方法&#xff0c;…

一站式数据可视化与分析平台JVS智能BI强大的数据节点功能

在商业智能&#xff08;BI&#xff09;中&#xff0c;数据集是数据的集合&#xff0c;用于分析和报告。数据节点是数据集中的一个重要组成部分&#xff0c;它代表数据集中的一个特定数据点或数据元素。通过使用数据节点&#xff0c;可以对数据进行过滤、分组和计算&#xff0c;…

Netty通信在中间件组件中的广泛使用-Dubbo3举例

Netty是一个高性能异步IO通信框架&#xff0c;封装了NIO&#xff0c;对各种bug做了很好的优化解决。所以很多中间件底层的通信都会使用Netty&#xff0c;比如说&#xff1a;Dubbo3&#xff0c;rocketmq&#xff0c;ElasticSearch等。 比方说&#xff0c;我们使用dubbo作为rpc跨…

批量混剪系统视频闪闪批量剪辑:只需几段素材片段即可批量混剪大量成片,快速制作大量成片的秘密

视频闪闪批量混剪系统&#xff1a;快速制作大量成片的秘密 在今天这个视频内容爆炸的时代&#xff0c;如何快速处理大量的素材并生成优质的成片&#xff0c;是许多视频制作人员面临的挑战。而视频闪闪批量混剪系统&#xff0c;却能帮助你轻松解决这一难题。 视频闪闪批量混剪…

Qt多工程同名字段自动翻译工具

开发背景 项目里不同工程经常会引用同一批公共类&#xff0c;这些类里如果有字段需要翻译&#xff0c;需要在不同的项目里都翻译一遍&#xff0c;比较麻烦冗余。 特此开发了这个小翻译工具&#xff0c;能读取程序目录下的所有ts文件&#xff0c;以类名归类&#xff0c;不同项目…

登陆认证权限控制(1)——从session到token认证的变迁 session的问题分析 + CSRF攻击的认识

前言 登陆认证&#xff0c;权限控制是一个系统必不可少的部分&#xff0c;一个开放访问的系统能否在上线后稳定持续运行其实很大程度上取决于登陆认证和权限控制措施是否到位&#xff0c;不然可能系统刚刚上线就会夭折。 本篇博客回溯登陆认证的变迁历史&#xff0c;阐述sess…

查找算法 —— 斐波拉契查找法

一、介绍 斐波拉契查找法是以分割范围进行查找的&#xff0c;分割的方式是按照斐波拉契级数的方式来分割。好处是&#xff1a;只用到加减运算&#xff0c;计算效率较高一些。 要使用斐波拉契查找首先需要定义一颗斐波拉契查找树&#xff0c;建立规则如下&#xff1a; 1.斐波拉契…

德国鞋履品牌【Birkenstock】申请15亿美元纳斯达克IPO上市

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 猛兽财经获悉&#xff0c;德国鞋履品牌【Birkenstock】近期已向美国证券交易委员会&#xff08;SEC&#xff09;提交招股书&#xff0c;申请在纳斯达克IPO上市&#xff0c;股票代码为&#xff08;BIRK&#xff09;,Birkens…

Vue-1.9工程化开发和脚手架

开发Vue的两种方式&#xff1a; 1.核心包传统开发模式&#xff1a;基于html/css/js文件&#xff0c;直接引入核心包&#xff0c;开发Vue 2.工程化开发模式&#xff1a;基于构建工具&#xff08;例如&#xff1a;webpack&#xff09;的环境中开发Vue 问题&#xff1a; 1&…

如何安装TortoiseSVN并实现公网提交文件至本地SVN服务器?

文章目录 前言1. TortoiseSVN 客户端下载安装2. 创建检出文件夹3. 创建与提交文件4. 公网访问测试 前言 TortoiseSVN是一个开源的版本控制系统&#xff0c;它与Apache Subversion&#xff08;SVN&#xff09;集成在一起&#xff0c;提供了一个用户友好的界面&#xff0c;方便用…

设计模式16、中介者模式 Mediator

解释说明&#xff1a;中介者模式&#xff08;Mediator Pattern&#xff09;用一个中介对象来封装一系列的对象交互 中介者使各对象不需要显式地相互引用&#xff0c;从而使其耦合松散&#xff0c;而且可以独立地改变它们之间的交互。 抽象中介者&#xff08;Mediator&#xff0…

Kaadas凯迪仕助力亚运盛会,尽展品牌硬核科技与智能锁行业风采

9月23日至10月8日&#xff0c;亚洲最大规模体育赛事亚运会在杭州举办。作为国际性体育赛事&#xff0c;除赛中的各类竞赛项目外&#xff0c;杭州亚运会前后相关活动也吸引了大众目光的聚焦。 Kaadas凯迪仕智能锁作为此次杭州亚运会官方指定智能门锁&#xff0c;以#凯迪仕守护每…

IP真人识别方法与代理IP检测技术

随着互联网的发展&#xff0c;IP地址在网络安全和数据分析中扮演着重要的角色。为了维护网络的安全性和识别真实用户&#xff0c;IP地址的真实性和来源成为了一个关键问题。 什么是IP真人识别&#xff1f; IP真人识别是一种技术&#xff0c;旨在确定IP地址背后的用户是否为真实…