docker-compose搭建minio对象存储服务器

docker-compose搭建minio对象存储服务器

最近想使用oss对象存储进行用户图片上传的管理,了解了一下例如aliyun或者腾讯云的oss对象存储服务,但是呢涉及到对象存储以及经费有限的缘故,决定自己手动搭建一个oss对象存储服务器;

首先大致了解一下对象存储:

对象存储OSS(Object Storage Service)是一种云存储服务,它提供了海量、安全、低成本、高可靠的存储解决方案

然后在经过大致了解后,选择了MiNiO,进行oss对象服务器的搭建工作,MinIO是一个开源的对象存储服务器,它兼容Amazon S3 API,并提供高性能、高可用性的存储解决方案。在本文中,我们将介绍如何使用Docker Compose快速部署MinIO。

一、docker-compose中的minio对象服务部署

准备工作:

1、服务器必须安装docker

2、服务器必须安装docker对应版本的docker-compose

1.1获取镜像:

首先,如果你的docker还能连上网,能够通过docker pull相关的镜像(咳咳,最近docker不对劲,拉取不到镜像),如果可以拉取镜像,可以执行下述命令:

docker pull minio/minio:latest

如果不可以,建议在往上下载一个minio的tar包,上传至服务器后,可以执行以下命令:

docker load -i minio.tar  ## minio 是你自己tar包的名字。

通过上述操作后,可以使用 docker images 进行查看获取到的镜像
请添加图片描述

1.2 docker-compose.yml文件制作

vim docker-compose.yml

先贴一个代码叭,一会儿挨个儿解释:

version: '3'
services:minio:image: minio/miniocontainer_name: minioports:- 9010:9000- 9011:9011environment:TZ: Asia/ShanghaiMINIO_ACCESS_KEY: minioMINIO_SECRET_KEY: minio123volumes:- ./data:/datacommand: server /data --console-address ":9011"

大概配置如上所示,有几点注意

注:

1、minio容器默认使用两个端口,9000和9001 9000端口主要适用于数据传输,9001端口主要是用于管理界面,上述文件中我为了好记且避免端口冲突,将9000端口映射到了服务器的9010端口,将9001端口改成了9011并映射到了服务器的9011端口

2、数据卷映射: 默认将数据卷映射到了docker-compose.yml同文件目录下的data文件夹

3、command: server --console-address ‘:9011’ /data 这行一定要加,否则端口号是随机的,你压根映射不出去

4、新版本中用户名和密码改用成了 “MINIO_ROOT_USER” 和 “MINIO_ROOT_PASSWORD” 旧版本是 “MINIO_ACCESS_KEY” 和 “MINIO_SECRET_KEY” 可以自己按照版本进行设置。

5、4中分别对应的是管理界面的用户名和密码

在编辑docker-compose.yml并保存后,通过下述命令创建并启动minio容器

#如果你的docker-compose.yml文件中有好几个容器,你并不想启动其他容器,只想启动minio
docker-compose up -d minio
#如果你的docker-compose.yml文件中只有目前的minio
docker-compose up -d 

启动成功后会看到服务器显示

请添加图片描述

此时可以在浏览器输入 上面docker-compose.yml文件中的【你自己的IP+9011】 访问minio的控制面板,记得开启防火墙9010,9011端口哟~

请添加图片描述

输入用户名和密码,登录minio控制面板

请添加图片描述

至此呢 单机版本 通过docker-compose 部署minio对象存储结束

二、spring-boot 集成 minio 对象存储

1、自己创建spring-boot 工程,在这里不多赘述

2、引入pom依赖

在自己的boot项目中引入minio依赖

<!---minio cos对象存储--><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.4.0</version></dependency>

3、集成代码

在集成代码之前呢,首先了解一下minio的几个知识点

  • Object:存储到Minio的基本对象,如文件、字节流、Anything…
  • Bucket:用来存储Object的逻辑空间。每个Bucket之间的数据量是互相隔离的。对于客户端而言,就相当于一个存放文件的顶层文件夹。
  • Drive:即存储数据的磁盘,在Minio启动时,以参数的方式传入。Minio中所有的对象数据都会存储在Drive里。
  • Set:即一组Drive的集合,分布式部署根据集群规模自动划分一个或多个Set,每个Set中的Drive分布在不同位置。一个对象存储在一个Set上.(for example:{1…64} is divided into 4 sets each of size 16)
    • 一个对象存储在一个Set上
    • 一个集群划分为多个Set
    • 一个Set包含的Drive数量是固定的,默认由系统根据集群规模自动计算得出
    • 一个Set中我的Drive尽可能分布在不同的节点上
3.1 创建用户,创建桶

可以在minio控制面板进行用户的创建以及存储桶(bucket)的创建。我们创建一个test的桶以及创建一个用户并赋予读写的权限

请添加图片描述

请添加图片描述

3.2 添加application.yml配置文件
minio:url: http://xxxxxxx #Minio服务所在地址bucketName: xxxxxx #存储桶名称accessKey: testUser #创建用户访问的key secretKey: 000000000 #创建用户 访问的秘钥
3.3 引入配置

创建MinioConfig 配置文件,将MinioClient 注入容器

@Data
@Configuration
@ConfigurationProperties(prefix = "minio")
public class MinioConfig {/*** 服务地址*/private String url;/*** 用户名*/private String accessKey;/*** 密码*/private String secretKey;/*** 存储桶名称*/private String bucketName;/*** 预览到期时间(小时)*/private Integer previewExpiry;@Beanpublic MinioClient getMinIOClient() {return MinioClient.builder().endpoint(url).credentials(accessKey, secretKey).build();}
}
3.4 引入相关操作

创建MinioCosManger文件 封装了minio客户端的一些操作

@Component
@Slf4j
public class MinioCosManger {@Autowiredprivate MinioConfig prop;@Resourceprivate MinioClient minioClient;/*** 查看存储bucket是否存在** @return boolean*/public Boolean bucketExists(String bucketName) {Boolean found;try {found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());} catch (Exception e) {e.printStackTrace();return false;}return found;}/*** 创建存储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** @return Boolean*/public Boolean removeBucket(String bucketName) {try {minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());} catch (Exception e) {e.printStackTrace();return false;}return true;}/*** 获取全部bucket*/public List<Bucket> getAllBuckets() {try {List<Bucket> buckets = minioClient.listBuckets();return buckets;} catch (Exception e) {e.printStackTrace();}return null;}/*** 文件上传** @param file 文件* @return Boolean*/public String upload(MultipartFile file) {String originalFilename = file.getOriginalFilename();if (StringUtils.isBlank(originalFilename)) {throw new RuntimeException();}String fileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));String dateFormat = "yyyy-MM/dd";DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat);LocalDate nowDate = LocalDate.now();String format = nowDate.format(formatter);String objectName = format + "/" + fileName;try {PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(prop.getBucketName()).object(objectName).stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();//文件名称相同会覆盖minioClient.putObject(objectArgs);} catch (Exception e) {e.printStackTrace();return null;}return objectName;}/*** 预览图片** @param fileName* @return*/public String preview(String fileName) {// 查看文件地址GetPresignedObjectUrlArgs build = new GetPresignedObjectUrlArgs().builder().bucket(prop.getBucketName()).object(fileName).method(Method.GET).build();try {String url = minioClient.getPresignedObjectUrl(build);return url;} catch (Exception e) {e.printStackTrace();}return null;}/*** 文件下载** @param fileName 文件名称* @param res      response* @return Boolean*/public void download(String fileName, HttpServletResponse res) {GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(prop.getBucketName()).object(fileName).build();try (GetObjectResponse response = minioClient.getObject(objectArgs)) {byte[] buf = new byte[1024];int len;try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {while ((len = response.read(buf)) != -1) {os.write(buf, 0, len);}os.flush();byte[] bytes = os.toByteArray();res.setCharacterEncoding("utf-8");// 设置强制下载不打开// res.setContentType("application/force-download");res.addHeader("Content-Disposition", "attachment;fileName=" + fileName);try (ServletOutputStream stream = res.getOutputStream()) {stream.write(bytes);stream.flush();}}} catch (Exception e) {e.printStackTrace();}}/*** 查看文件对象** @return 存储bucket内文件对象信息*/public List<Item> listObjects() {Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(prop.getBucketName()).build());List<Item> items = new ArrayList<>();try {for (Result<Item> result : results) {items.add(result.get());}} catch (Exception e) {e.printStackTrace();return null;}return items;}/*** 删除** @param fileName* @return* @throws Exception*/public boolean remove(String fileName) {try {minioClient.removeObject(RemoveObjectArgs.builder().bucket(prop.getBucketName()).object(fileName).build());} catch (Exception e) {return false;}return true;}}
3.5 创建controller进行测试
/*** @version 1.0* @Author jerryLau* @Date 2024/7/1 11:23* @注释*/
@Api(tags = "文件相关接口")
@Slf4j
@RestController
@RequestMapping(value = "product/file")
public class FileController2 {@Autowiredprivate MinioCosManger minioUtil;@Autowiredprivate MinioConfig prop;@ApiOperation(value = "查看存储bucket是否存在")@GetMapping("/bucketExists")public BaseResponse<String> bucketExists(@RequestParam("bucketName") String bucketName) {if (minioUtil.bucketExists(bucketName)) {return ResultUtils.success("bucketName is exit!");} elsereturn ResultUtils.error(ErrorCode.NOT_FOUND_ERROR, "bucketName is not exit!");}@ApiOperation(value = "创建存储bucket")@GetMapping("/makeBucket")public BaseResponse<String> makeBucket(String bucketName) {if (minioUtil.makeBucket(bucketName)) {return ResultUtils.success("create bucket success!");} elsereturn ResultUtils.error(ErrorCode.OPERATION_ERROR, "create bucket error!");}@ApiOperation(value = "删除存储bucket")@GetMapping("/removeBucket")public BaseResponse<String> removeBucket(String bucketName) {if (minioUtil.removeBucket(bucketName)) {return ResultUtils.success("removeBucket  success!");} elsereturn ResultUtils.error(ErrorCode.OPERATION_ERROR, "removeBucket error!");}@ApiOperation(value = "获取全部bucket")@GetMapping("/getAllBuckets")public BaseResponse<List<Bucket>> getAllBuckets() {List<Bucket> allBuckets = minioUtil.getAllBuckets();return ResultUtils.success(allBuckets);}@ApiOperation(value = "文件上传返回url")@PostMapping("/upload")public BaseResponse<String> upload(@RequestParam("file") MultipartFile file) {String objectName = minioUtil.upload(file);if (null != objectName) {String url = (prop.getUrl() + "/" + prop.getBucketName() + "/" + objectName);return ResultUtils.success(url);}return ResultUtils.error(ErrorCode.OPERATION_ERROR, "upload error!");}@ApiOperation(value = "图片/视频预览")@GetMapping("/preview")public BaseResponse<String> preview(@RequestParam("fileName") String fileName) {String preview = minioUtil.preview(fileName);return ResultUtils.success(preview);}@ApiOperation(value = "文件下载")@GetMapping("/download")public void download(@RequestParam("fileName") String fileName, HttpServletResponse res) {minioUtil.download(fileName, res);}@ApiOperation(value = "删除文件", notes = "根据url地址删除文件")@PostMapping("/delete")public BaseResponse<String> remove(String url) {String objName = url.substring(url.lastIndexOf(prop.getBucketName() + "/") + prop.getBucketName().length() + 1);boolean remove = minioUtil.remove(objName);if (remove) {return ResultUtils.success(objName + "delete success!");} else return ResultUtils.error(ErrorCode.OPERATION_ERROR, objName + "delete error!");}}
3.6 接口测试以及存储验证

通过knife4j或者其他请求测试工具(postman、apifox等),测试接口

请添加图片描述

注意:

1、按照理论来说在上传结束后返回的这个文件的url应该没有办法直接访问,应该在访问该存储对象的时候,去调用preview方法,但是对本人而言,调用preview返回的地址太长了,并且存在一定的时效性,在超过一段时间后将不会在被访问到,所以本人通过给bucket设置access prefix为 readandwrite,这样一来,上传接口的返回url便可直接被访问到了。

2、如果想去调用preview 或者download 方法时,所传入的文件名一定是bucket后面的全部文件名称,比如上面测试图片中,如果调用,传入文件名应为2024-07/01/0011c366-f2a4-4b26-adbc-931d444d7205.png 而不是简单的0011c366-f2a4-4b26-adbc-931d444d7205.png ,否则即使返回了preview的url ,这个url也无法被访问到。

请添加图片描述


至此,通过docker-compose手动搭建minio 对象存储服务器已全部完结,喜欢的观众老爷,请一键三连 🎉🎉🎉,感谢大家~

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

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

相关文章

创建XCOM窗体和跳转连接

Xcom 窗体&#xff1a; (groupBox组合框&#xff0c;comboBox下拉框) xcom代码&#xff1a; namespace _01_作业 {// 1kb 1024B 1200B// 1MB public partial class Form1 : Form{public List<string> botelv new List<string> { "600","1200&…

如何使用 SPM 插件从 Pkl 配置文件生成 Swift 接口

文章目录 前言示例展示 Pkl 配置生成 Swift 绑定手动安装和使用 pkl-gen-swift创建 SPM 命令插件加载 Pkl 配置总结前言 Pkl(全称为 Pickle)是苹果推出的一种全新的专用于配置的编程语言。它允许开发人员通过类型和内置验证安全、直观地设计数据模型。 作为苹果语言,Pkl 有…

Python容器 之 列表--下标和切片

列表的切片 得到是 新的列表字符串的切片 得到是 新的字符串 如果下标 不存在会报错 list1 [1, 3.14, "hello", False] print(list1)# 获取 列表中 第一个数据 print(list1[0]) # 1# 获取列表中的最后一个数据 print(list1[-1]) # [False]# 获取中间两个数 即 3.1…

3.2ui功能讲解之graph页面

本节重点介绍 : graph页面target页面flags页面status页面tsdb-status页面 访问地址 $ip:9090 graph页面 autocomplete 可以补全metrics tag信息或者 内置的关键字 &#xff0c;如sum聚合函数table查询 instante查询&#xff0c; 一个点的查询graph查询调整分辨率 resolutio…

Study--Oracle-05-Oracler体系结构

一、oracle 体系概览 Oracle数据库的体系结构通常包括以下主要组件&#xff1a; 1、实例&#xff08;Instance&#xff09;&#xff1a;运行数据库的软件环境&#xff0c;包括内存结构&#xff08;SGA&#xff09;和进程结构&#xff08;Background Processes and User Proces…

Django 一对多关系

1&#xff0c;创建 Django 应用 Test/app9 django-admin startapp app9 2&#xff0c;注册应用 Test/Test/settings.py 3&#xff0c;添加应用路由 Test/Test/urls.py from django.contrib import admin from django.urls import path, includeurlpatterns [path(admin/,…

数据资产安全策略的定制化之道:深入了解各企业独特需求,量身打造个性化的数据资产保护方案,确保数据安全无虞,助力企业稳健发展

目录 一、引言 二、企业数据资产安全现状分析 &#xff08;一&#xff09;数据安全风险多样化 &#xff08;二&#xff09;传统安全措施难以满足需求 &#xff08;三&#xff09;企业数据资产安全意识亟待提高 三、定制化数据资产安全策略的重要性 &#xff08;一&#…

natvicat为什么连不上linux上的mysql?

老规矩&#xff0c;废话不多说&#xff0c;直接上教程。 号外&#xff0c;数据库管理工具领域的知名品牌Navicat&#xff0c;推出其免费版本——Navicat Premium Lite&#xff0c;用户可从Navicat官网下载体验这款软件。 https://www.navicat.com.cn/download/navicat-premium-…

【HALCON】如何实现hw窗口自适应相机拍照成像的大小

前言 在开发一个喷码检测软件的时候碰到相机成像和hw窗体的大小不一致&#xff0c;hw太小显示不完全成像的图片&#xff0c;这使得成像不均匀&#xff0c;现场辨别起来比较不直观&#xff0c;因此需要对其进行一个调整。 解决 省略掉读取图片的环节&#xff0c;我们只需要将…

ruoyi—cloud 新建模块+生成代码

1.复制一个模块——修改名字 2.打开模块下的yml文件&#xff0c;修改端口号和名字 &#xff08;1&#xff09;修改一个名字 &#xff08;2&#xff09;打开yml文件 &#xff08;3&#xff09;修改端口号&#xff0c;不要重复 &#xff08;4&#xff09;改名字和模块一致 3.…

41、web基础和http协议

web基础与http协议 一、web web&#xff1a;就是我们所说得页面&#xff0c;打开网页展示得页面。&#xff08;全球广域网&#xff0c;万维网&#xff09; world wide webwww 分布式图形信息系统 http&#xff1a;超文本传输协议 https&#xff1a;加密的超文本传输协议…

猫冻干可以天天喂吗?喂冻干前要了解的必入主食冻干榜单

近年来&#xff0c;冻干猫粮因其高品质而备受喜爱&#xff0c;吸引了无数猫主人的目光&#xff0c;对于像我这样的养猫达人来说&#xff0c;早已尝试并认可了冻干喂养。然而&#xff0c;对于初入养猫行列的新手们来说&#xff0c;可能会有疑问&#xff1a;什么是冻干猫粮&#…

Qt——界面优化

目录 QSS 基本语法 QSS 设置方式 指定控件样式设置 全局样式设置 文件加载样式表 Qt Designer 编辑样式 选择器 子控件选择器 伪类选择器 样式属性 盒模型 控件样式 按钮 复选框 单选框 输入框 列表 菜单栏 登录界面 绘图 概念 绘制形状 绘制线段 绘制…

微信换手机号了怎么绑定新手机号?

微信换手机号了怎么绑定新手机号&#xff1f; 1、在手机上找到并打开微信&#xff1b; 2、打开微信后&#xff0c;点击底部我的&#xff0c;并进入微信设置&#xff1b; 3、在微信设置账号与安全内&#xff0c;找到手机号并点击进入&#xff1b; 4、选择更换手机号&#xff0c…

淘系-万相台无界实操运营课:淘系 付费工具课(40节课)

课程目录 01_万相台无界系统性忖费推广思维.mp4 02_万相台无界七大推广场景详解.mp4 03关键词推广计划之标准计划搭建技巧.mp4 04_关键词推广之智能计划推广技巧.mp4 05_关键词推广之趋势选品计划推广技巧.mp4 06关键词推广之智能选品计划推广技巧.mp4 07_非标品的关键词…

待办工作如何在桌面分区显示

在现代快节奏的工作环境中&#xff0c;我们每天都要处理大量的待办事项。面对这些繁多的事项&#xff0c;很多人常常感到无从下手&#xff0c;导致工作任务堆积&#xff0c;影响工作效率。那么&#xff0c;如何在繁杂的事项中保持清晰&#xff0c;让工作更有条理呢&#xff1f;…

旋转变压器软件解码simulink仿真

1.介绍 旋转变压器是一种精密的位置、速度检测装置&#xff0c;尤其适用于高温、严寒、潮湿、高速、振动等环境恶劣、旋转编码器无法正常工作的场合。旋转变压器在使用时并不能直接提供角度或位置信息&#xff0c;需要特殊的激励信号和解调、计算措施&#xff0c;才能将旋转变压…

Hamster (CHO) PLBL2 ELISA Kit—仓鼠(CHO) PLBL2 ELISA试剂盒

宿主细胞蛋白&#xff08;HCP&#xff09;是生物制药过程中产生的一类主要杂质&#xff0c;是重组疫苗及重组抗体类药物的重要质控指标。虽然大部分HCP可以在早期的纯化步骤中除去&#xff0c;但是仍有一些HCP会通过纯化系统携带&#xff0c;可以躲过常规HCP ELISA检测。ICL的H…

RedHat9 | podman容器-续集

一、管理容器存储和网络资源 使用容器来运行简单的进程&#xff0c;然后退出。可以配置容连续运行特定服务&#xff0c;如数据库服务。如果持续运行服务&#xff0c;需要向容器添加更多的资源&#xff0c;如持久存储或对其他网络的访问权限。 针对企业容器平台上的大型部署&a…

为Ubuntu-24.04-live-server-amd64磁盘扩容

系列文章目录 Ubuntu-24.04-live-server-amd64安装界面中文版 文章目录 系列文章目录前言一、检查系统本身情况1.用 lsblk 命令查看自己系统磁盘是什么状态2.用 df -h 命令查看文件系统的磁盘空间使用情况3.解决 Ubuntu-24.04 磁盘空间只能用一半的问题3-1扩展逻辑卷&#xff…