MinIO入门-02 SpringBoot 整合MinIO并实现文件上传

article/2025/10/5 22:23:35

SpringBoot 整合MinIO并实现文件上传

1、依赖

<!-- https://mvnrepository.com/artifact/io.minio/minio -->
<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.3.9</version>
</dependency><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>4.9.1</version>
</dependency>

2、连接参数说明

你需要有存储服务的三个参数才能连接到该服务。

参数说明
Endpoint对象存储服务的URL
Access KeyAccess key就像用户ID,可以唯一标识你的账户。
Secret KeySecret key是你账户的密码。
bucketNamebucketName是你设置的桶的名称

3、yaml配置

server:port: 18001
spring:application:name: minio-applicationservlet:multipart:max-file-size: 10MBmax-request-size: 10MB
minio:endpoint: http://192.168.211.132:9000accessKey: adminsecretKey: admin123456bucketName: minio-test

4、加载Minio配置 MinioConfig.java

package cn.lyf.minio.config;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;/*** @author lyf* @description: Minio 配置类* @version: v1.0* @since 2022-04-29 14:49*/
@Configuration
@Data
@ConfigurationProperties(value = "minio")
public class MinioConfig {/*** 对象存储服务的URL*/private String endpoint;/*** Access key就像用户ID,可以唯一标识你的账户。*/private String accessKey;/*** Secret key是你账户的密码。*/private String secretKey;/*** bucketName是你设置的桶的名称*/private String bucketName;/*** 初始化一个MinIO客户端用来连接MinIO存储服务** @return MinioClient*/@Beanpublic MinioClient initMinioClient() {return MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();}
}

5、分布式ID生成方案IdWorker (代码来源于网络)

package cn.lyf.minio.entity;import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;/*** <p>名称:IdWorker.java</p>* <p>描述:分布式自增长ID</p>* <pre>*     Twitter的 Snowflake JAVA实现方案* </pre>* 核心代码为其IdWorker这个类实现,其原理结构如下,我分别用一个0表示一位,用—分割开部分的作用:* 1||0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---000000000000* 在上面的字符串中,第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间,* 然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识),* 然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。* 这样的好处是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分),* 并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。* <p>* 64位ID (42(毫秒)+5(机器ID)+5(业务编码)+12(重复累加))** @author Polim*/
public class IdWorker {// 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)private final static long twepoch = 1649410310176L;// 机器标识位数private final static long workerIdBits = 5L;// 数据中心标识位数private final static long datacenterIdBits = 5L;// 机器ID最大值private final static long maxWorkerId = ~(-1L << workerIdBits);// 数据中心ID最大值private final static long maxDatacenterId = ~(-1L << datacenterIdBits);// 毫秒内自增位private final static long sequenceBits = 12L;// 机器ID偏左移12位private final static long workerIdShift = sequenceBits;// 数据中心ID左移17位private final static long datacenterIdShift = sequenceBits + workerIdBits;// 时间毫秒左移22位private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;private final static long sequenceMask = ~(-1L << sequenceBits);/* 上次生产id时间戳 */private static long lastTimestamp = -1L;// 0,并发控制private long sequence = 0L;private final long workerId;// 数据标识id部分private final long datacenterId;public IdWorker(){this.datacenterId = getDatacenterId(maxDatacenterId);this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);}/*** @param workerId*            工作机器ID* @param datacenterId*            序列号*/public IdWorker(long workerId, long datacenterId) {if (workerId > maxWorkerId || workerId < 0) {throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));}if (datacenterId > maxDatacenterId || datacenterId < 0) {throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));}this.workerId = workerId;this.datacenterId = datacenterId;}/*** 获取下一个ID** @return*/public synchronized long nextId() {long timestamp = timeGen();if (timestamp < lastTimestamp) {throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));}if (lastTimestamp == timestamp) {// 当前毫秒内,则+1sequence = (sequence + 1) & sequenceMask;if (sequence == 0) {// 当前毫秒内计数满了,则等待下一秒timestamp = tilNextMillis(lastTimestamp);}} else {sequence = 0L;}lastTimestamp = timestamp;// ID偏移组合生成最终的ID,并返回IDlong nextId = ((timestamp - twepoch) << timestampLeftShift)| (datacenterId << datacenterIdShift)| (workerId << workerIdShift) | sequence;return nextId;}private long tilNextMillis(final long lastTimestamp) {long timestamp = this.timeGen();while (timestamp <= lastTimestamp) {timestamp = this.timeGen();}return timestamp;}private long timeGen() {return System.currentTimeMillis();}/*** <p>* 获取 maxWorkerId* </p>*/protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {StringBuffer mpid = new StringBuffer();mpid.append(datacenterId);String name = ManagementFactory.getRuntimeMXBean().getName();if (!name.isEmpty()) {/** GET jvmPid*/mpid.append(name.split("@")[0]);}/** MAC + PID 的 hashcode 获取16个低位*/return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);}/*** <p>* 数据标识id部分* </p>*/protected static long getDatacenterId(long maxDatacenterId) {long id = 0L;try {InetAddress ip = InetAddress.getLocalHost();NetworkInterface network = NetworkInterface.getByInetAddress(ip);if (network == null) {id = 1L;} else {byte[] mac = network.getHardwareAddress();id = ((0x000000FF & (long) mac[mac.length - 1])| (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;id = id % (maxDatacenterId + 1);}} catch (Exception e) {System.out.println(" getDatacenterId: " + e.getMessage());}return id;}/*public static void main(String[] args) {//推特  26万个不重复的ID*//*IdWorker idWorker = new IdWorker(0,0);for (int i = 0; i <2600 ; i++) {System.out.println(idWorker.nextId());}*//*System.out.println(maxDatacenterId);System.out.println(maxWorkerId);System.out.println(System.currentTimeMillis());}*/}

5.1、注入IDWorker

    /*** 分布式ID生成方案,用于生成文件名称** @return id*/@Beanpublic IdWorker idWorker() {return new IdWorker(0, 0);}

6、文件上传的代码 FileController.java

package cn.lyf.minio.controller;import cn.lyf.minio.config.MinioConfig;
import cn.lyf.minio.entity.IdWorker;
import io.minio.*;
import io.minio.errors.MinioException;
import io.minio.http.Method;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;
import java.util.concurrent.ThreadPoolExecutor;/*** @author lyf* @description:* @version: v1.0* @since 2022-05-03 10:39*/
@RestController
@RequestMapping(value = "/file")
@Slf4j
public class FileController {@Autowiredprivate MinioConfig minioConfig;@Autowiredprivate MinioClient minioClient;@Autowiredprivate IdWorker idWorker;@Resource(name = "minioFileThreadPool")private ThreadPoolExecutor minioFileThreadPool;/*** 文件上传** @param file file* @return 上传之后的文件地址*/@PostMapping(value = "/upload")public String upload(@RequestParam(name = "file") MultipartFile file) throws Exception {// 检查存储桶是否已经存在boolean isExist =minioClient.bucketExists(BucketExistsArgs.builder().bucket(minioConfig.getBucketName()).build());if (isExist) {log.info("Bucket {} already exists.", minioConfig.getBucketName());} else {// 创建一个名为asiatrip的存储桶,用于存储照片的zip文件。minioClient.makeBucket(MakeBucketArgs.builder().bucket(minioConfig.getBucketName()).build());}String originalFilename = file.getOriginalFilename();String filenameExtension = StringUtils.getFilenameExtension(originalFilename);String fileNameInMinio = idWorker.nextId() + "." + filenameExtension;// 使用putObject上传一个文件到存储桶中。minioClient.putObject(PutObjectArgs.builder()// 设置桶名称.bucket(minioConfig.getBucketName())// 设置文件流对象.stream(file.getInputStream(), file.getSize(), -1)// 设置文件在minio服务器中的名称.object(fileNameInMinio)// 设置文件类型.contentType(file.getContentType()).build());log.info("file upload success, filename: {}, contentType: {}, size(byte): {}", fileNameInMinio,file.getContentType(), file.getSize());// 获取文件名称String presignedObjectUrl = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()// 文件请求方式.method(Method.GET)// 对应的桶的名称.bucket(minioConfig.getBucketName())// 对应的文件名.object(fileNameInMinio).build());// TODO 可以异步的将文件信息存入数据库return presignedObjectUrl;}
}

7、测试

image-20220503111144122

http://192.168.211.132:9000/minio-test/8962006962929664.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20220503%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20220503T030339Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=178facc151eb409b283270dc2fd1210a3eb1162e4cadcc600a6f6e033e60680c


http://chatgpt.dhexx.cn/article/QT7EA5wB.shtml

相关文章

Invalid bound statement (not found): com.lyf.eduservice.mapper.EduCourseMapper.getpublishInfo

目录 问题分析&#xff1a; 解决方案&#xff1a; 问题分析&#xff1a; dao层编译后只有class文件&#xff0c;没有mapper.xml&#xff0c;因为maven工程在默认情况下src/main/java目录下的所有资源文件是不发布到target目录下的 解决方案&#xff1a; 第一步、在项目的po…

ELF

目录 一&#xff0c;目标文件格式 二&#xff0c;ELF 1&#xff0c;分段 2&#xff0c;工具 3&#xff0c;查看目标文件内容 一&#xff0c;目标文件格式 编译链接 https://blog.csdn.net/nameofcsdn/article/details/116654835 目标文件和可执行文件的逻辑结构是类似的…

littlefs

1、littlefs主要用在微控制器和flash上&#xff0c;是一种嵌入式文件系统。主要有3个特点&#xff1a; 1)、掉电恢复 在写入时即使复位或者掉电也可以恢复到上一个正确的状态。 2)、擦写均衡 有效延长flash的使用寿命 3)、有限的RAM/ROM 节省ROM和RAM空间 2、已有的文件系…

luffy-(13)

内容概览 支付宝支付介绍支付宝支付二次封装订单相关表设计生成订单接口支付前端支付宝回调接口 支付宝支付介绍 """ 项目中有需要在线支付功能,可以使用支付宝支付(沙箱环境)微信支付(需要有备案过的域名)云闪付我们的项目以支付宝支付为例支付流程使用官方…

【FLFL】

论文记录 1. 3.3《基于区块链的联邦学习技术综述》2. 3.4《Swarm Learning for decentralized and confidential clinical machine learning》3. 3.8《Blockchained On-Device Federated Learning》4. 3.11《FLchain: Federated Learning via MEC-enabled Blockchain Network》…

LYF95101A 是一款高性能、高集成度、具有快速

LYF95101A 概述 LYF95101A是一款高性能、高集成度、具有快速 关断特性的单通道同步整流控制器。支持CCM, QR和 DCM的多模式工作。通过智能的控制MOSFET的开通 和关断&#xff0c;可替代反激变换器次级整流的肖特基二极管 来实现效率的提高。 LYF95101A 内置自供电电路&#xff…

渗透测试简介

病毒&#xff1a;是在计算机程序中插入的破坏计算机功能或者数据的代码&#xff0c;能影响计算机使用&#xff0c;能自我复制的一组计算机指令或者程序代码&#xff1b; ●木马&#xff1a;是比较流行的病毒文件&#xff0c;与一般的病毒不同&#xff0c;它不会自我繁殖&#x…

介绍模糊测试(Fuzz Testing,Fuzzing)

介绍模糊测试&#xff08;Fuzz Testing&#xff0c;Fuzzing&#xff09; 一、什么是模糊测试&#xff1f; 模糊测试是一种自动或半自动的测试技术&#xff0c;常被用来发现软件/操作系统/网络的代码中的错误和安全性问题&#xff0c;其中用于输入随机的数据和不合法的数据被称…

#学习笔记4#软件测试基础——测试阶段划分、黑盒测试的一些知识

今天主要看了以下几个方面的知识点&#xff0c;基本都是纯理论&#xff0c;本文只做知识点总结&#xff0c;具体内容要看转载 1.软件测试阶段划分&#xff0c;分为4个阶段&#xff1a;单元测试、集成测试、系统测试、验收测试 单元测试是方法类的覆盖&#xff0c;主要是由开…

辅助程序实现黑盒自动化测试的常见问题

背景 辅助程序&#xff08;Accessibility&#xff09;在大多数机型上具有重启设备后被激活的特性&#xff0c;可以完成Android测试框架&#xff08;Uiautomator1.0、Uiautomator2.0&#xff09;无法实现的功能。本文介绍如何搭建辅助程序和如何利用辅助程序进行黑盒测试。并总…

Android 13 Camera ITS 环境搭建(从Python安装到环境配置详解)

Python 版本 根据CameraITS.pdf 以及报错信息提示需要python版本在3.7.9及以上&#xff0c;本次使用的是3.8.10。 Python 安装 1.官方网站下载Python源码 wget https://www.python.org/ftp/python/3.8.10/Python-3.8.10.tgz 2.进入到下载路径&#xff0c;解压Python文件 …

黑盒测试常见错误类型说明及解决方法有哪些?

目录 1、用户界面错误 2、遗漏信息 3、错误的、误导的或令人迷惑的信息 1、用户界面错误 功能性 易用性&#xff08;用户学习使用程序的时间和记住怎样使用程序的时间&#xff09; 执行速度&#xff08;多数是启动速度&#xff0c;查询速度&#xff0c;刷新速度及响应速度…

浅析黑盒测试与白盒测试

这里写自定义目录标题 黑盒测试黑盒测试常见的测试&#xff1a;黑盒测试常用的方法&#xff1a;等价类划分&#xff1a;边界值分析&#xff1a;因果图分析法、错误推断法&#xff1a; 白盒测试白盒测试常见的方法&#xff1a; 黑盒测试与白盒测试的优缺点二者的优点二者的缺点 …

卸载symantec杀毒软件

(Get-WmiObject -Class Win32_Product -Filter “Name‘Symantec Endpoint Protection’” -ComputerName . ).Uninstall()

schrodinger 薛定谔安装与卸载

schrodinger 薛定谔安装 Windows 版 百度云 链接&#xff1a;https://pan.baidu.com/s/107a4KMHMvg1vrXTFHnGcWw 提取码&#xff1a;c6t4 安装步骤 download所有压缩包&#xff0c;解压任意一个压缩包即可&#xff08;压缩包之间是相互关联的&#xff09;。 进入解压的后的文…

从rookie到基佬~009:无密码卸载Symantec

今天是变直小技巧 今日份洗脑&#xff1a;无密码情况下卸载Symantec&#xff08;赛门铁克&#xff09; 结论&#xff1a;赛门铁克的服务一旦启动&#xff0c;怎么卸载都需要密码&#xff0c;反过来说&#xff0c;你不让他启动&#xff0c;他就是待宰的羔羊。 坐标国内某企业…

赛门铁克下载

Symantec&#xff08;赛门铁克&#xff09;杀毒软件&#xff0c;包括Symantec AntiVirus即SAV系列&#xff0c;Symantec Client Security即SCS系列&#xff0c;以及Symantec Endpoint Protection即SEP系列&#xff0c;都是专门为企业级用户定制的。这3套Symantec杀毒软件均包括…

Symantex Endpoint Protection赛门铁克杀毒软件安装

一、交付邮件 目前软件交付基本都是通过邮件进行。杀毒软件交付邮件中附件包含序列号以及购买产品信息。 二、官网软件下载 在右键中点击“Download Today”跳转下载页面&#xff08;吐槽&#xff1a;交互很差劲&#xff09; 登录网站&#xff1a;https://support.broadcom.…

如何卸载赛门铁克(Symantec)企业防病毒客户端软件SEP(Symantec Endpoint Protection)?

本文分三个部分介绍如何卸载赛门铁克&#xff08;Symantec&#xff09;企业防病毒客户端软件SEP的Windows版本&#xff0c;Linux版本和MacOS版本。 1 卸载SEP for Windows 版本 SEP for Windows版本卸载使用传统办法 -- 通过Windows控制面板卸载是比较容易出问题的&#xff0…

赛门铁克管理密码忘记卸载工具

链接: https://pan.baidu.com/s/1zoolODBkK_ytHh4gPvHFxw?pwd2eea 提取码: 2eea 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 卸载工具已附上 1、安装步骤简单&#xff0c;不多解释。 工具安装到此步骤就可以看到卸载的进度。