spring boot 整合 minio存储 【使用篇】

news2025/2/26 5:06:04

zi导入依赖

		<!--minio-->
		<dependency>
			<groupId>io.minio</groupId>
			<artifactId>minio</artifactId>
			<version>8.0.3</version>
		</dependency>

yml配置(默认配置)

spring:
  # 配置文件上传大小限制
  servlet:
    multipart:
      max-file-size: 200MB
      max-request-size: 200MB
minio:
  endpoint: http://127.0.0.1:9000
  accessKey: minioadmin
  secretKey: minioadmin
  bucketName: bucket-qhj

一一对应

补充:由于每次启动minio.exe要进入对应目录,很麻烦,所以可以写个批处理文件.bat

D:  代表进入D盘

cd D:\Program Files\minio 代表进入你minio.exe存放的根目录

minio.exe server D:\MineFile\zuoye\xm\minioFile --console-address ":9999" 在D:\MineFile\zuoye\xm\minioFile文件夹执行,并且增加额外端口’9999‘

@echo off
echo.
echo [信息] 运行MinIO文服务器。
echo.
 
title minio
 
D:
cd D:\Program Files\minio
 
minio.exe server D:\MineFile\zuoye\xm\minioFile --console-address ":9999"
pause

配置类

注入客户端配置放入bean

使用value注解装配yml配置文件中的值

@Data
@Component
public class MinIoClientConfig {
    @Value("${minio.endpoint}")
    private String endpoint;
    @Value("${minio.accessKey}")
    private String accessKey;
    @Value("${minio.secretKey}")
    private String secretKey;

    /**
     * 注入minio 客户端
     * @return
     */
    @Bean
    public MinioClient minioClient(){

        return MinioClient.builder()
                .endpoint(endpoint)
                .credentials(accessKey, secretKey)
                .build();
    }

}

实体类

@Data
public class ObjectItem {
    private String objectName;
    private Long size;
}

工具类

根据实体类

/**
 * @description: minio工具类
 * @version:3.0
 */
@Component
public class MinioUtilS {
    @Autowired
    private MinioClient minioClient;

    @Value("${minio.bucketName}")
    private String bucketName;
    /**
     * description: 判断bucket是否存在,不存在则创建
     *
     * @return: void
     */
    public void existBucket(String name) {
        try {
            boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build());
            if (!exists) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建存储bucket
     * @param bucketName 存储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
     * @param bucketName 存储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;
    }
    /**
     * description: 上传文件
     *
     * @param multipartFile
     * @return: java.lang.String

     */
    public List<String> upload(MultipartFile[] multipartFile) {
        List<String> names = new ArrayList<>(multipartFile.length);
        for (MultipartFile file : multipartFile) {
            String fileName = file.getOriginalFilename();
            String[] split = fileName.split("\\.");
            if (split.length > 1) {
                fileName = split[0] + "_" + System.currentTimeMillis() + "." + split[1];
            } else {
                fileName = fileName + System.currentTimeMillis();
            }
            InputStream in = null;
            try {
                in = file.getInputStream();
                minioClient.putObject(PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(fileName)
                        .stream(in, in.available(), -1)
                        .contentType(file.getContentType())
                        .build()
                );
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            names.add(fileName);
        }
        return names;
    }

    /**
     * description: 下载文件
     *
     * @param fileName
     * @return: org.springframework.http.ResponseEntity<byte [ ]>
     */
    public ResponseEntity<byte[]> download(String fileName) {
        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, HttpStatus.OK);
        } 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 bucketName 存储bucket名称
     * @return 存储bucket内文件对象信息
     */
    public List<ObjectItem> listObjects(String bucketName) {
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).build());
        List<ObjectItem> objectItems = new ArrayList<>();
        try {
            for (Result<Item> result : results) {
                Item item = result.get();
                ObjectItem objectItem = new ObjectItem();
                objectItem.setObjectName(item.objectName());
                objectItem.setSize(item.size());
                objectItems.add(objectItem);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return objectItems;
    }

    /**
     * 批量删除文件对象
     * @param bucketName 存储bucket名称
     * @param objects 对象名称集合
     */
    public Iterable<Result<DeleteError>> removeObjects(String bucketName, List<String> objects) {
        List<DeleteObject> dos = objects.stream().map(e -> new DeleteObject(e)).collect(Collectors.toList());
        Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build());
        return results;
    }
}

控制器类

返回可访问的url

@RestController
@Slf4j
@RequestMapping("/fileSave")
public class MinioController {
    @Autowired
    private MinIoUtil minIoUtil;
    @Autowired
    private MinioUtilS minioUtilS;
    @Value("${minio.endpoint}")
    private String address;
    @Value("${minio.bucketName}")
    private String bucketName;

    @RequestMapping("/fileInMinio")
    public R uploadInMinio(MultipartFile file) {
        List<String> upload = minioUtils.upload(new MultipartFile[]{file});
        String url = address + "/" + bucketName + "/" + upload.get(0);
        return R.ok().put("filePath", url);
    }

}

上传后获取链接为

访问报错

解决方式

来到minio控制台

打开你正在使用的bucket

修改访问权限为public就行

重新访问链接,应该就能查看

前端代码

通过父组件导入子组件,注册子组件,引用子组件弹出

父组件调用方法

    // 文件导入
    importExcel () {
      this.uploadFileVisible = true
      this.$nextTick(() => {
        this.$refs.uploadFile.init()
      })
    }

 子组件vue页面

<template>
  <el-dialog
    title="维修处理"
    :close-on-click-modal="true"
    :visible.sync="visible"
  >
    <el-select
      size="small"
      v-model="uploadMethodValue"
      placeholder="请选择上传方式"
      clearable=""
      filterable
    >
      <el-option
        v-for="d in uploadMethod"
        :key="d.value"
        :label="d.name"
        :value="d.value"
      ></el-option>
    </el-select>

    <el-upload
      class="upload-demo"
      ref="upload"
      drag
      action="#"
      :on-change="handleChangeSelect"
      :on-exceed="handleExceed"
      :file-list="fileList"
      :limit="1"
      multiple
      :auto-upload="false"
    >
      <i class="el-icon-upload"></i>
      <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
      <div
        class="el-upload__tip"
        slot="tip"
      >只能上传jpg/png文件,且不超过500kb</div>
      <div
        class="el-upload__tip"
        slot="tip"
      >访问路径:{{ filePath }}</div>
    </el-upload>
  </el-dialog>
</template>
    
    <script>
export default {
  data () {
    return {
      // 对话框显示状态
      visible: false,
      uploadMethod: [
        { name: '本地存储', value: '1' },
        { name: 'oss存储', value: '2' },
        { name: 'miniio', value: '3' }
      ],
      // 选中的上传方式
      uploadMethodValue: '',
      // 表单数据
      dataForm: {
      },
      filePath: '',
      fileList: []
    }
  },
  methods: {
    // 初始化方法
    init () {
      this.visible = true
      this.filePath = ''
    },
    // 文件超出个数提示
    handleExceed (files, fileList) {
      this.$message.warning(`当前限制选择 1 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`)
    },
    handleChangeSelect (file) {
      if (this.uploadMethodValue === '1') {
        // 本地存储
        this.handleChange(file, '/fileSave/file')
      } else if (this.uploadMethodValue === '2') {
        // oss存储
        this.handleChange(file, '/fileSave/fileInOSS')
      } else if (this.uploadMethodValue === '3') {
        // miniio
        this.handleChange(file, '/fileSave/fileInMinio')
      }
    },
    handleChange (file, urlSelect) {
      let formData = new FormData()
      formData.append('file', file.raw) // 传文件
      this.$http({
        url: this.$http.adornUrl(urlSelect),
        method: 'post',
        data: formData,
        headers: {
          'Content-Type': 'multipart/form-data'
        }
      }).then(({ data }) => {
        if (data && data.code === 0) {
          this.filePath = data.filePath
          this.$message({
            message: '操作成功',
            type: 'success',
            duration: 1500
          })
        } else {
          this.$message.error(data.msg)
        }
        this.$refs.upload.clearFiles() // 清除文件
      })
    }
  }
}
  </script>

拓展学习

MultipartFile

 MultipartFile为org.springframework.web.mutipart包下的一个类,也就是说如果想使用MultipartFile这个类就必须引入spring框架,换句话说,如果想在项目中使用MultipartFile这个类,那么项目必须要使用spring框架才可以,否则无法引入这个类。

一般来讲使用MultipartFile这个类主要是来实现以表单的形式进行文件上传功能。

MiltipartFile类注释说明

 第一句:一种可以接收使用多种请求方式来进行上传文件的代表形式。也就是说,如果你想用spring框架来实现项目中的文件上传功能,则MultipartFile可能是最合适的选择,而这里提到的多种请求方式则可以通俗理解为以表单的形式提交。

      第二句:这个文件内容可以存储到内存中或者存储在磁盘的临时位置上。

      第三句:无论发生哪种情况,用户都可以自由地拷贝文件内容到session存储中,或者以一种永久存储的形式进行存储,如果有需要的话。

      第四句:这种临时性的存储在请求结束之后将会被清除掉。

类中方法

 getName方法

获取的是前后端约定的传入文件的参数的名称,在SpringBoot后台中则是通过@Param("uploadFile") 注解定义的内容。

getOriginalFileName方法 

getOriginalFileName方法获取的是文件的完整名称,包括文件名称+文件拓展名。

getContentType方法 

getContentType方法获取的是文件的类型,注意是文件的类型,不是文件的拓展名。

getBytes方法 

getBytes方法用来将文件转换成一种字节数组的方式进行传输,会抛出IOException异常。

getInputStream方法 

getInputStream方法用来将文件转换成输入流的形式来传输文件,会抛出IOException异常。

transferTo方法 

transferTo方法用来将接收文件传输到给定目标路径,会抛出IOException、IllegalStateException异常。该方法在实际项目开发中使用较少。

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

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

相关文章

vue -- watermark水印添加方法

前言 项目生成公司水印是很普遍的需求&#xff0c;下面是vue项目生产水印的方法。话不多说&#xff0c;复制粘贴就可以马上解决你的需求。 步骤1 创建watermark.js文件。目录结构 /** 水印添加方法 */let setWatermark (str1, str2) > {let id 1.23452384164.1234124…

配电房轨道式巡检机器人方案

一、应用背景 在变电站、配电房、开关站等各种室内变配电场所内&#xff0c;由于变配电设备的数量众多、可能存在各类安全隐患&#xff0c;为了保证用电的安全可靠&#xff0c;都要进行日常巡检。 但目前配电房人工巡检方式有以下主要问题&#xff1a; 巡检工作量大、成本高 …

无法导入ohos.bundle.installer错误解决方法

今天在运行一个开源项目时&#xff0c;发现编译项目时报了一个错误&#xff1a;ohos.bundle.installer。 对应的SDK版本信息如下&#xff1a; 解决方法&#xff1a; 造成错误的原因是&#xff0c;我们使用的是public-sdk&#xff0c;所以我们需要到OpenHarmony平台下载full-s…

配电房智能辅助监控系统设计

业务背景 工业企业、学校、医院、居民小区等单位有这海量的配电房&#xff0c;这些配电房内的配电设备种类多、运行环境复杂&#xff0c;存在各种各样的安全隐患。目前这些配电房主要依靠人员在场值守或巡检方式进行管理&#xff0c;但单纯的人工运维方式既成本高&#xff0c;…

第3届图像处理与媒体计算国际会议(ICIPMC 2024)即将召开!

2024年第3届图像处理与媒体计算国际会议&#xff08;ICIPMC2024&#xff09;将于2024年5月17-19日在中国合肥举行。本次大会由安徽大学、西北工业大学&#xff0c;西北大学和IEEE联合主办。ICIPMC 2024旨在汇集该领域领先的学术科学家、研究人员和学者&#xff0c;并进行交流和…

精品springboot校园失物招领系统

《[含文档PPT源码等]精品基于springboot校园失物招领系统[包运行成功]》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功&#xff01; 软件开发环境及开发工具&#xff1a; Java——涉及技术&#xff1a; 前端使用技术&#xff1a;HTML5,…

Linux磁盘如何分区?

首先需要先给虚拟机添加磁盘 sblk #查看磁盘设备 得到以下内容&#xff1a; NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 20G 0 disk ├─sda1 8:1 0 1G 0 part /boot └─sda2 8:2 0 19G 0 pa…

杰理701N可视化SDK之EQ在线调试

杰理701N可视化SDK EQ在线调试 准备工具声学调音步骤软件导入stream.bin在线调试功能蓝牙连接失败问题 杰理JL701N可视化SDK工具中支持在线调试EQ, 对EQ进行实时调整, 可以更方便地控制和塑造声音改善或定制声音效果. EQ即均衡器(Equalizer, 简称EQ), 是一种音效处理工具, 它允…

ConcurrentHashMap的演进:从Java 8之前到Java 17的实现原理深度剖析

目录 一、引言二、Java 8之前的ConcurrentHashMap1、内部结构与初始化2、Segment类3、并发控制4、扩容与重哈希5、总结 三、Java 8中的ConcurrentHashMap1、数据结构2、并发控制2.1. CAS操作2.2. synchronized同步块 3、哈希计算与定位4、扩容与重哈希5、总结 四、Java 17中的C…

【计算机图形学】Where2Act: From Pixels to Actions for Articulated 3D Objects

文章目录 1.论文做了件什么事儿2. 论文为什么要做这件事3. 介绍Introduction4. 相关工作预测语义表达推理几何和物体属性从被动观察中学习Affordance从交互中学习感知 5. 问题陈述6. 方法6.1 网络模块主干特征提取器可运动性评分模块运动建议模块运动评分模块 6.2 训练数据收集…

深入解剖指针(6)

个人主页&#xff08;找往期文章包括但不限于本期文章中不懂的知识点&#xff09;&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 目录 二维数组 指针运算笔试题解析 接着上篇文章的题目&#xff08;所有的代码运行环境若无特殊说明&#xff0c;则都是VS2022&#xff0c;x64环境下…

公益src | 一次简单的验证码绕过

本文由掌控安全学院 -好好好 投稿 1、在漏洞平台&#xff0c;公益SRC上&#xff0c;找一个网站&#xff0c;找到登录处 2、抓包&#xff0c;发现密码明文&#xff0c;放到Repeater&#xff0c;多次G&#xff4f;&#xff0c;发现没有验证码&#xff0c;也不限制次数 &…

CBAM注意力机制详解(附pytorch复现)

简介 论文原址&#xff1a;1807.06521.pdf (arxiv.org) CBAM&#xff08;Convolutional Block Attention Module&#xff09;是一种卷积神经网络模块&#xff0c;旨在通过引入注意力机制来提升网络的表示能力。CBAM包含两个顺序子模块&#xff1a;通道注意力模块和空间注意力…

视频拉流推流技术梳理

概况 视频的整个流程主要分为推流和拉流 摄像头场景&#xff1a; 摄像头捕捉视频画面&#xff0c;推流到服务器&#xff0c;服务器分发到CDN&#xff0c; 客户端从CDN地址拉流&#xff0c;客户端进行播放 直播场景&#xff1a; 主播通过手机&#xff0c;电脑等客户端&…

强化学习(六)时序差分

时序差分&#xff08;TD&#xff09;是强化学习的核心&#xff0c;其是蒙特卡罗&#xff08;MC&#xff09;和动态规划&#xff08;DP&#xff09;的结合。 1、TD 预测 TD 和 MC 都是利用经验来解决预测问题。一种非平稳环境的一般访问蒙特卡罗方法是 V ( S t ) ← V ( S t …

力扣-H指数

问题 给你一个整数数组 citations &#xff0c;其中 citations[i] 表示研究者的第 i 篇论文被引用的次数。计算并返回该研究者的 h 指数。 根据维基百科上 h 指数的定义&#xff1a;h 代表“高引用次数” &#xff0c;一名科研人员的 h 指数 是指他&#xff08;她&#xff09…

android开发平台,Java+性能优化+APP开发+NDK+跨平台技术

开头 通常作为一个Android APP开发者&#xff0c;我们并不关心Android的源代码实现&#xff0c;不过随着Android开发者越来越多&#xff0c;企业在筛选Android程序员时越来越看中一个程序员对于Android底层的理解和思考&#xff0c;这里的底层主要就是Android Framewok中各个组…

【Linux深入剖析】再续环境变量 | 进程地址空间

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 目录 1.环境变量再续1.1 和…

图书管理系统(使用IO流实现数据的读取和写入)--version4.0

目录 一、项目要求&#xff1a; 二、项目环境 三、项目使用的知识点 四、项目代码 五、项目运行结果 六、项目难点分析 图书管理系统--versions1.0&#xff1a; 图书管理系统--versions1.0-CSDN博客文章浏览阅读981次&#xff0c;点赞29次&#xff0c;收藏17次。本文使用…

Encoding, Encryption, Tokenization 傻傻分不清楚

Encoding, Encryption, Tokenization 傻傻分不清楚 本文转自 公众号 ByteByteGo&#xff0c;如有侵权&#xff0c;请联系&#xff0c;立即删除 今天来聊聊编码 (Encoding), 加密 (Encryption) 和 令牌化 (Tokenization) 的区别。 编码、加密和标记化是三种不同的流程&#xff…