【微服务】springboot整合minio详解
目录
一、前言
二、Minio 概述
2.1 Minio简介
2.1 Minio特点
三、Minio 环境搭建
3.1 部署过程
3.1.1 拉取镜像
3.1.2 启动容器
3.1.3 访问web页面
四、Minio基本使用
4.1 基本概念
4.2 上传文件演示
4.3 用户管理
4.4 Java操作Minio
4.4.1 导入依赖
4.4.2 上传文件到minio
五、springboot整合Minio
5.1 前置准备
5.1.1 引入依赖
5.1.2 核心配置文件
5.2 编码过程
5.2.1 创建一个参数配置类
5.2.2 创建minio配置类
5.2.3 创建minio文件服务类或工具类
5.2.4 编写测试接口
5.2.5 接口测试
六、写在文末
一、前言
在很多互联网产品应用中,都涉及到各种与文件存储相关的业务,随着技术的发展,关于如何解决分布式文件存储也有了比较成熟的方案,比如私有云部署下可以考虑fastdfs,阿里云对象存储oss,七牛云等,本篇将为你介绍另一种文件存储方式,即MinIO 。
二、Minio 概述
2.1 Minio简介
MinIO基于Apache License v2.0开源协议的对象存储服务,可以做为云存储的解决方案用来保存海量的图片,视频,文档,是一款高性能、分布式的对象存储系统, 可以100%的运行在标准硬件,即X86等低成本机器也能够很好的运行MinIO。
传统的存储和其他的对象存储不同的是:
它一开始就针对性能要求更高的私有云标准进行软件架构设计。因为MinIO一开始就只为对象存储而设计。所以他采用了更易用的方式进行设计,它能实现对象存储所需要的全部功能,在性能上也更加强劲,它不会为了更多的业务功能而妥协,失去MinIO的易用性、高效性。 这样的结果所带来的好处是:它能够更简单的实现局有弹性伸缩能力的原生对象存储服务。
2.1 Minio特点
Minio具有如下特点
- 性能高,准硬件条件下它能达到55GB/s的读、35GB/s的写速率;
- 部署自带管理界面;
- MinIO.Inc运营的开源项目,社区活跃度高;
- 提供了所有主流开发语言的SDK;
- 基于Golang语言实现,配置简单,单行命令可以运行起来;
- 兼容亚马逊S3云存储服务接口,适合于存储大容量非结构化的数据,一个对象文件可以是任意大小,从几kb到最大5T不等;
三、Minio 环境搭建
本文采用docker的方式快速搭建起Minio的环境,也可以通过官网下载安装包部署,官网安装包下载地址
3.1 部署过程
3.1.1 拉取镜像
docker pull minio/minio
3.1.2 启动容器
docker run -d -p 9000:9000 -p 9090:9090 \--name minio \-e "MINIO_ACCESS_KEY=minio" \-e "MINIO_SECRET_KEY=minio" \-v /home/minio/data:/data \-v /home/minio/config:/root/.minio \minio/minio server \/data --console-address ":9090" -address ":9000"
3.1.3 访问web页面
容器启动成功后,注意开发相关的防火墙端口即可,然后访问地址:IP:9000,即可访问Minio的web界面
输入账户和密码,登录进去之后,看到下面的效果说明Minio环境搭建完成
四、Minio基本使用
4.1 基本概念
在正式开始使用Minio之前,有必要先了解下几个相关的概念
- bucket ,类比于文件系统的目录;
- Object ,类比文件系统的文件;
- Keys ,类比文件名;
4.2 上传文件演示
如下,点击创建一个新的bucket,创建完成后就可以在列表上看到这个bucket;
给当前这个bucket上传一个文件
点击文件夹图标
上传一张本地文件,上传完成后就可以看到这个文件了,也可以浏览上传的文件
4.3 用户管理
针对客户端的操作,经常需要维护相关的账号来管理,比如账户的操作权限,访问控制等;
点击,创建用户
填写用户信息,勾选权限保存即可
然后在用户列表就可以看到这个用户了
4.4 Java操作Minio
通过上面的环境搭建和操作,演示并了解了如何快速使用Minio,更多的功能可以参阅相关资料进一步学习了解,下面我们编写java代码完成文件的上传。
4.4.1 导入依赖
在当前的maven工程中导入minio的依赖,客户端具体版本可以根据你的实际需要选择
<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>7.1.0</version></dependency>
4.4.2 上传文件到minio
通过下面这段代码将本地的一张图片文件上传到minio的test这个bucket目录下
public static void main(String[] args) {FileInputStream inputStream = null;try {inputStream = new FileInputStream("F:\\网盘\\Title-logo.png");MinioClient client = MinioClient.builder().credentials("minio", "minio").endpoint("http://IP:9000").build();PutObjectArgs putObjectArgs = PutObjectArgs.builder().object("logo.png").contentType("image/png").bucket("test").stream(inputStream, inputStream.available(), -1).build();client.putObject(putObjectArgs);System.out.println("上传成功");} catch (Exception e) {e.printStackTrace();} finally {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}}
运行这段代码,上传成功后,去到test的目录下刷新之后可以看到文件已经上传
五、springboot整合Minio
接下来让我们看看如何在springboot中集成Minio,参考下面的操作步骤
5.1 前置准备
5.1.1 引入依赖
创建一个maven工程,引入如下相关的依赖
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.4.RELEASE</version></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId></dependency><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>7.1.0</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency></dependencies>
5.1.2 核心配置文件
在配置文件中添加如下内容
server:port: 8087logging:level:root: infocom.congge.model: debugminio:username: miniopwd: miniobucket: testendpoint: http://IP:9000readPath: http://IP:9000
5.2 编码过程
5.2.1 创建一个参数配置类
通过这种方式将配置文件中以minio开头的那些配置加载到spring容器中管理,其他位置使用的时候直接注入即可
@Data
@ConfigurationProperties(prefix = "minio")
@Component
public class MinIOConfigProperties implements Serializable {private String username;private String pwd;private String bucket;private String endpoint;private String readPath;}
5.2.2 创建minio配置类
通过这个全局的配置类,其他需要上传文件的类中直接注入MinioClient即可
import io.minio.MinioClient;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Data
@Configuration
public class MinIOConfig {@Autowiredprivate MinIOConfigProperties minIOConfigProperties;@Beanpublic MinioClient buildMinioClient() {return MinioClient.builder().credentials(minIOConfigProperties.getAccessKey(), minIOConfigProperties.getSecretKey()).endpoint(minIOConfigProperties.getEndpoint()).build();}
}
5.2.3 创建minio文件服务类或工具类
在日常开发中,可以自定义一个minio的工具类,使用的时候就比较方便了,下面的代码中列举了常用的一些API操作方法,可以结合实际需要自定义更多的工具方法
import com.congge.config.MinIOConfig;
import com.congge.config.MinIOConfigProperties;
import com.congge.service.MinioFileService;
import io.minio.*;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.util.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;@Slf4j
@Import(MinIOConfig.class)
@Service
public class MinioFileServiceImpl implements MinioFileService {@Autowiredprivate MinioClient minioClient;@Autowiredprivate MinIOConfigProperties minIOConfigProperties;private final static String separator = "/";/*** 下载文件** @param pathUrl 文件全路径* @return 文件流*/@Overridepublic void downLoadFile(String pathUrl, HttpServletResponse response) {String[] pathItems = pathUrl.split("/");String originFileName = pathItems[pathItems.length - 1];String key = pathUrl.replace(minIOConfigProperties.getEndpoint() + "/", "");int index = key.indexOf(separator);//String bucket = key.substring(0,index);String filePath = key.substring(index + 1);InputStream inputStream = null;try {inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(minIOConfigProperties.getBucket()).object(filePath).build());response.setHeader("Content-Disposition", "attachment;filename=" + originFileName);response.setContentType("application/force-download");response.setCharacterEncoding("UTF-8");IOUtils.copy(inputStream, response.getOutputStream());System.out.println("下载成功");} catch (Exception e) {log.error("minio down file error. pathUrl:{}", pathUrl);e.printStackTrace();} finally {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}}@Overridepublic String uploadFile(MultipartFile file) throws Exception {String bucketName = minIOConfigProperties.getBucket();String endpoint = minIOConfigProperties.getEndpoint();// 检查存储桶是否已经存在boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());if (isExist) {System.out.println("Bucket already exists.");} else {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());}String originalFilename = file.getOriginalFilename();//拼接生成新的UUID形式的文件名String objectName = new SimpleDateFormat("yyyy/MM/dd/").format(new Date()) +UUID.randomUUID().toString().replaceAll("-", "")+ originalFilename.substring(originalFilename.lastIndexOf("."));PutObjectArgs objectArgs = PutObjectArgs.builder().object(objectName).bucket(bucketName).contentType(file.getContentType()).stream(file.getInputStream(), file.getSize(), -1).build();minioClient.putObject(objectArgs);//组装桶中文件的访问urlString resUrl = endpoint + "/" + bucketName + "/" + objectName;return resUrl;}/*** 删除文件** @param pathUrl 文件全路径*/@Overridepublic void delete(String pathUrl) {String key = pathUrl.replace(minIOConfigProperties.getEndpoint() + "/", "");int index = key.indexOf(separator);String bucket = key.substring(0, index);String filePath = key.substring(index + 1);// 删除ObjectsRemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder().bucket(bucket).object(filePath).build();try {minioClient.removeObject(removeObjectArgs);} catch (Exception e) {log.error("minio remove file error. pathUrl:{}", pathUrl);e.printStackTrace();}}public List<Bucket> listBuckets()throws Exception {return minioClient.listBuckets();}public boolean bucketExists(String bucketName) throws Exception {boolean flag = minioClient.bucketExists(bucketName);if (flag) {return true;}return false;}@Overridepublic List<String> listBucketNames() throws Exception{List<Bucket> bucketList = listBuckets();List<String> bucketListName = new ArrayList<>();for (Bucket bucket : bucketList) {bucketListName.add(bucket.name());}return bucketListName;}@Overridepublic boolean makeBucket(String bucketName) throws Exception{boolean flag = bucketExists(bucketName);if (!flag) {minioClient.makeBucket(bucketName);return true;} else {return false;}}@Overridepublic boolean removeBucket(String bucketName) throws Exception{boolean flag = bucketExists(bucketName);if (flag) {Iterable<Result<Item>> myObjects = listObjects(bucketName);for (Result<Item> result : myObjects) {Item item = result.get();// 有对象文件,则删除失败if (item.size() > 0) {return false;}}// 删除存储桶,注意,只有存储桶为空时才能删除成功。minioClient.removeBucket(bucketName);flag = bucketExists(bucketName);if (!flag) {return true;}}return false;}@Overridepublic List<String> listObjectNames(String bucketName) throws Exception{List<String> listObjectNames = new ArrayList<>();boolean flag = bucketExists(bucketName);if (flag) {Iterable<Result<Item>> myObjects = listObjects(bucketName);for (Result<Item> result : myObjects) {Item item = result.get();listObjectNames.add(item.objectName());}}return listObjectNames;}@Overridepublic boolean removeObject(String bucketName, String objectName) throws Exception{boolean flag = bucketExists(bucketName);if (flag) {List<String> objectList = listObjectNames(bucketName);for (String s : objectList) {if(s.equals(objectName)){minioClient.removeObject(bucketName, objectName);return true;}}}return false;}@Overridepublic String getObjectUrl(String bucketName, String objectName) throws Exception{boolean flag = bucketExists(bucketName);String url = "";if (flag) {url = minioClient.getObjectUrl(bucketName, objectName);}return url;}public Iterable<Result<Item>> listObjects(String bucketName) throws Exception {boolean flag = bucketExists(bucketName);if (flag) {return minioClient.listObjects(bucketName);}return null;}}
5.2.4 编写测试接口
为了方便测试,下面定义了一个测试接口
import com.congge.service.MinioFileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletResponse;
import java.util.List;@RestController
@Slf4j
public class FileController {@Autowiredprivate MinioFileService minioFileService;/*** 上传文件* @param file* @return* @throws Exception*/@PostMapping("/upload")public String upload(@RequestBody MultipartFile file) throws Exception {String url = minioFileService.uploadFile(file);return "文件上传成功,文件路径:" + url;}/*** 下载文件* @param pathUrl* @param response*/@GetMapping("/download")public void download(@RequestParam("pathUrl") String pathUrl, HttpServletResponse response) {minioFileService.downLoadFile(pathUrl,response);}/*** 列出所有bucket名称* @return* @throws Exception*/@PostMapping("/list/bucket")public List<String> list() throws Exception {return minioFileService.listBucketNames();}/*** 创建bucket* @param bucketName* @return* @throws Exception*/@PostMapping("/create/bucket")public boolean createBucket(@RequestParam("bucketName")String bucketName) throws Exception {return minioFileService.makeBucket(bucketName);}/*** 删除bucket* @param bucketName* @return* @throws Exception*/@PostMapping("/delete/bucket")public boolean deleteBucket(@RequestParam("bucketName")String bucketName) throws Exception {return minioFileService.removeBucket(bucketName);}/*** 列出bucket的所有对象名称* @param bucketName* @return* @throws Exception*/@PostMapping("/list/object_names")public List<String> listObjectNames(@RequestParam("bucketName")String bucketName) throws Exception {return minioFileService.listObjectNames(bucketName);}/*** 删除bucket中的某个对象* @param bucketName* @param objectName* @return* @throws Exception*/@PostMapping("/remove/object")public boolean removeObject(@RequestParam("bucketName")String bucketName,@RequestParam("objectName") String objectName) throws Exception {return minioFileService.removeObject(bucketName, objectName);}/*** 获取文件访问路径* @param bucketName* @param objectName* @return* @throws Exception*/@PostMapping("/get/object/url")public String getObjectUrl(@RequestParam("bucketName")String bucketName, @RequestParam("objectName")String objectName) throws Exception {return minioFileService.getObjectUrl(bucketName, objectName);}}
5.2.5 接口测试
上传文件测试
上传成功后,返回了文件的完整路径,方便后续使用
然后在控制台的test这个bucket中检查是否上传上去了
下载文件测试
使用上一步返回的url,直接调用下载接口,下载完成后可以直接预览
更多的接口有兴趣的同学可以一一尝试下,就不再赘述了。
六、写在文末
本文详细总结了Minio的搭建使用,以及与springboot整合的完整步骤,作为一款适用且轻量级的文件存储服务器,可以私有化部署,也可以很方便进行云上部署、容器化部署,为今后在实际项目中的技术选型提供一个参考,本篇到此结束,感谢观看。
相关文章:

【微服务】springboot整合minio详解
目录 一、前言 二、Minio 概述 2.1 Minio简介 2.1 Minio特点 三、Minio 环境搭建 3.1 部署过程 3.1.1 拉取镜像 3.1.2 启动容器 3.1.3 访问web页面 四、Minio基本使用 4.1 基本概念 4.2 上传文件演示 4.3 用户管理 4.4 Java操作Minio 4.4.1 导入依赖 4.4.2 上传…...

减速机振动相关标准 - 笔记
参考标准:国家标准|GB/T 39523-2020 减速机的振动标准与发动机不同,摘引: 原始加速度传感器波形 可以明显看到调幅波 它的驱动电机是300Hz~2000Hz范围的。这个采样时间是5秒,看分辨率至少1024线。可分出500条谱线。 频谱部分 …...
【matlab】MATLAB 中的标量运算及实例
MATLAB 中的标量运算及实例 引言 在 MATLAB 中,标量是指只包含单个数值的变量或常量。尽管标量运算可能看似简单,但它在数值计算、数据处理和算法设计中扮演着重要的角色。本文将深入探讨 MATLAB 中的标量运算,介绍其基本操作和一些实例应用。 1. 标量运算的基本操作 标…...

java简易制作-王者荣耀游戏
一.准备工作 首先创建一个新的Java项目命名为“王者荣耀”,并在src下创建两个包分别命名为“com.sxt"、”com.stx.beast",在相应的包中创建所需的类。 创建一个名为“img”的文件夹来储存所需的图片素材。 二.代码呈现 package com.sxt; import javax…...

手撕分布式缓存---多节点的调取
经过上一个章节的学习,我们已经知晓了如何搭建了HTTP Server,通过HTTP协议与我们定义的路由,我们可以远程访问这个节点;基于这一点,我们可以部署多台实现了HTTP的缓存服务从而实现分布式的特性。这一章节我们要基于此背…...

C/C++编程中的算法实现技巧与案例分析
C/C编程语言因其高效、灵活和底层的特性,被广大开发者用于实现各种复杂算法。本文将通过10个具体的算法案例,详细探讨C/C在算法实现中的技巧和应用。 一、冒泡排序(Bubble Sort) 冒泡排序(Bubble Sort)是一…...

干货分享 | 如何在TSMaster中对常用总线报文信号进行过滤?
TSMaster软件平台支持对不同总线(CAN、LIN、FlexRay)的报文和信号过滤,过滤方法一般有全局接收过滤、数据流过滤、窗口过滤、字符串过滤、可编程过滤,针对不同的总线信号过滤器的使用方法也基本相同。今天重点和大家分享一下关于T…...

k8s链接数据库故障Waiting for table metadata lock
场景:早上来发现一个程序,链接mysql数据库有点问题,随后排查,因为容器在k8s里面。所以尝试重启了pod没有效果 一、重启pod: 这里是几种在Kubernetes中重启Pod的方法: 删除Pod,利用Deployment重建 kubectl delete pod mypodDepl…...

数字经济如何驱动企业高质量发展? ——核心机制、模式选择与推进路径
文章目录 每日一句正能量前言核心机制信息化和智能化作为数字经济的核心机制信息化和智能化如何提升企业生产效率和管理水平数据的获取、分析和利用对企业发展的影响 模式选择电子商务模式的选择共享经济模式的选择数据驱动的业务模式选择 推进路径建设数字化基础设施培养数字化…...
机器学习——支持向量机
目录 一、基于最大间隔分隔数据 二、寻找最大间隔 1. 最大间隔 2. 拉格朗日乘子法 3. 对偶问题 三、SMO高效优化算法 四、软间隔 五、SMO算法实现 1. 简化版SMO算法 2. 完整版SMO算法 3. 可视化决策结果 六、核函数 1. 线性不可分——高维可分 2. 核函数 …...
mq的作用
使用mq优点 mq是一种常见的中间件,在项目中经常用到,它具有异步、解耦、削峰填谷的作用。 异步 比如下单流程,A服务—>B服务,总的耗时是A耗时时间B耗时时间,而改为A—>mq---->B后,A发送mq后立刻…...

AUTOSAR组织引入了Rust语言的原因是什么?有哪些好处?与C++相比它有什么优点?并推荐一些入门学习Rust语言链接等
AUTOSAR(汽车开放系统架构)是一个由汽车制造商、供应商和其他来自电子、半导体和软件行业的公司组成的全球发展伙伴关系,自2003年以来一直致力于为汽车行业开发和引入开放、标准化的软件平台。 AUTOSAR 最近宣布成立一个新的工作组,用于探索在汽车软件中使用 Rust 编程语言…...

基于PyCharm实现串口GUI编程
工具效果如下如所示 下面简单介绍一下操作流程 1.打开PyCharm软件 2.创建一个工程 3.给该工程命名 4.在main.py里面黏贴如下的代码 # This is a sample Python script. # Press ShiftF10 to execute it or replace it with your code. # Press Double Shift to search everyw…...

【1.8计算机组成与体系结构】磁盘管理
目录 1.磁盘基本结构与存取过程1.1 磁盘基本结构1.2 磁盘的存取过程 2.磁盘优化分布存储3.磁盘单缓冲区与双缓冲区4.磁盘移臂调度算法 1.磁盘基本结构与存取过程 1.1 磁盘基本结构 磁盘:柱面,磁道,扇区。 1.2 磁盘的存取过程 存取时间寻…...
1663:【 例 1】取石子游戏 1
【题目描述】 有一种有趣的游戏,玩法如下: 玩家: 2 人; 道具: N 颗石子; 规则: 1、游戏双方轮流取石子; 2、每人每次取走若干颗石子(最少取 1 颗,最多取…...
Django去访问web api接口Object of type Session is not JSON serializable
解决方案:settings.py中加入 :SESSION_SERIALIZER django.contrib.sessions.serializers.PickleSerializer 事由:Django去访问一个web api接口,两次连接之间需要通过Session()保持身份验证。 def sendCode(request): mobile jso…...

每日一题,二维平面
给你 二维 平面上两个 由直线构成且边与坐标轴平行/垂直 的矩形,请你计算并返回两个矩形覆盖的总面积。 每个矩形由其 左下 顶点和 右上 顶点坐标表示: 第一个矩形由其左下顶点 (ax1, ay1) 和右上顶点 (ax2, ay2) 定义。 第二个矩形由其左下顶点 (bx1, …...
【jupyter notebook】jupyter notebook 调用另一个jupyter notebook 的函数
总结 使用 %run 魔法命令将 Notebook 转换为py文件使用 nbimporter 库手动复制代码优点notebook最前面加上即可最基本方法就跟导入py文件一样,不会被执行一遍快缺点所有的代码都会执行一遍修改原文件就要重新转换,且 从自定义的 .py 文件中导入函数时&a…...

Linux--学习记录(3)
G重要编译参数 -g(GDB调试) -g选项告诉gcc产生能被GNU调试器GDB使用的调试信息,以调试程序编译带调试信息的可执行文件g -g hello.c -o hello编译过程: -E(预处理) g -E hello.c -o hello.i-S(编…...

自然语言处理阅读第一弹
Transformer架构 encoder和decoder区别 Embeddings from Language Model (ELMO) 一种基于上下文的预训练模型,用于生成具有语境的词向量。原理讲解ELMO中的几个问题 Bidirectional Encoder Representations from Transformers (BERT) BERT就是原生transformer中的Encoder两…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...