• minio高可用 架构与实操(图解+秒懂+史上最全)


    文章很长,建议收藏起来,慢慢读! Java 高并发 发烧友社群:疯狂创客圈 奉上以下珍贵的学习资源:


    推荐:入大厂 、做架构、大力提升Java 内功 的 精彩博文

    入大厂 、做架构、大力提升Java 内功 必备的精彩博文 2021 秋招涨薪1W + 必备的精彩博文
    1:Redis 分布式锁 (图解-秒懂-史上最全) 2:Zookeeper 分布式锁 (图解-秒懂-史上最全)
    3: Redis与MySQL双写一致性如何保证? (面试必备) 4: 面试必备:秒杀超卖 解决方案 (史上最全)
    5:面试必备之:Reactor模式 6: 10分钟看懂, Java NIO 底层原理
    7:TCP/IP(图解+秒懂+史上最全) 8:Feign原理 (图解)
    9:DNS图解(秒懂 + 史上最全 + 高薪必备) 10:CDN图解(秒懂 + 史上最全 + 高薪必备)
    11: 分布式事务( 图解 + 史上最全 + 吐血推荐 ) 12:seata AT模式实战(图解+秒懂+史上最全)
    13:seata 源码解读(图解+秒懂+史上最全) 14:seata TCC模式实战(图解+秒懂+史上最全)

    Java 面试题 30个专题 , 史上最全 , 面试必刷 阿里、京东、美团... 随意挑、横着走!!!
    1: JVM面试题(史上最强、持续更新、吐血推荐) 2:Java基础面试题(史上最全、持续更新、吐血推荐
    3:架构设计面试题 (史上最全、持续更新、吐血推荐) 4:设计模式面试题 (史上最全、持续更新、吐血推荐)
    17、分布式事务面试题 (史上最全、持续更新、吐血推荐) 一致性协议 (史上最全)
    29、多线程面试题(史上最全) 30、HR面经,过五关斩六将后,小心阴沟翻船!
    9.网络协议面试题(史上最全、持续更新、吐血推荐) 更多专题, 请参见【 疯狂创客圈 高并发 总目录

    SpringCloud 精彩博文
    nacos 实战(史上最全) sentinel (史上最全+入门教程)
    SpringCloud gateway (史上最全) 更多专题, 请参见【 疯狂创客圈 高并发 总目录

    背景:

    下一个视频版本,从架构师视角,尼恩为大家打造高可用、高并发中间件的原理与实操。

    目标:通过视频和博客的方式,为各位潜力架构师,彻底介绍清楚架构师必须掌握的高可用、高并发环境,包括但不限于:

    • 高可用、高并发nginx架构的原理与实操

    • 高可用、高并发mysql架构的原理与实操

    • 高可用、高并发nacos架构的原理与实操

    • 高可用、高并发rocketmq架构的原理与实操

    • 高可用、高并发es架构的原理与实操

    • 高可用、高并发minio架构的原理与实操

    在这里插入图片描述

    why 高可用、高并发中间件的原理与实操:

    • 实际的开发过程中,很多小伙伴聚焦crud开发,环境出了问题,都不能启动。

    • 作为架构师,或者未来想走向高端开发,或者做架构,必须掌握高可用、高并发中间件的原理,掌握其实操。

    本系列博客的具体内容,请参见 Java 高并发 发烧友社群:疯狂创客圈

    分布式文件系统应用场景

    ​ 互联网下海量的非结构化存储的需求背景下,比如:

    • 电商网站,存储海量的商品图片
    • 视频网站,海量的视频文件
    • 网盘,海量的文件
    • 社交网站等等

    ​ 在这样的背景下,传统的FastDFS部署太过于繁琐,动不动就是来个nginx,然后配置一堆参数和设置,尤其是做分布式的时候,那维护成本一下就上来了,从维护和部署的角度,FastDFS不是一个好的选择,而从迭代的角度,FastDFS早就不维护了,有很多需求是无法

    支持到的,那么就需要你自己思考写源码打包了。

    同理HDFS部署也不简单,而且Hadoop适合超大文件的存储,并且文件都需要分片,应用场景更多是计算处理,实时数据分析,并且其实HDFS比较吃硬件设备,因为偏于计算,所以对CPU的要求比较高,对于中小企业的业务量并没有这么大,所以应用场景这块也比较

    难接触到,但是HDFS的功能还是十分强大的!!还是根据业务进行选型。

    存储方案的选型

    以下选型方案,来自于探探

    这是探探app的几个功能,它的基础功能非常简单左划右划,左划是不喜欢,右划是喜欢,当两个人相互喜欢就配对成功了,配对成功之后就开始相互聊天。

    在这里插入图片描述

    你所看到的一些图片、视频还有在聊天中产生的语音,这些元素在对象存储的范畴里面,就是以对象保存下来。

    随着用户量的增长,探探所要存储的对象越来越多。

    探探存储的文件类型

    在这里插入图片描述

    这是探探2019年的文件存储数据.

    • 数据的体量为:

    在不同类型对象所占的空间中,显而易见图片是存储的一个大头,超过了 1PB,它定期清理所以大小基本上保持了稳定的状态。

    • 访问的吞吐量数据为:

    写入的 QPS 大概是 1 千左右,读取是 5 千左右,压力不是很大。

    方案选型

    在这里插入图片描述

    于是,探探团队就开始调研当前的一些开源方案,这些存储方案里面可以分为两种:

    • 一种是可以自定对象名称的;

    • 另外一种是系统自动生成对象名称。

    探探的对象名是自己生成的,里面包含了业务逻辑。

    像 FS 就是国内大佬开源的一个分支存储,但是因为不能自定义文件名所以不合适,左划掉。

    还有像领英的 Ambry、MogileFS 其实都不能自定对象名的,所以是不合适的。

    左上角 LeoFS 对探探来说不是很可控,所以不合适。

    TFS 是淘宝开源的,但是目前已经很少有人维护它并且也不是很活跃,所以当时就没有考虑。

    ceph 是一个比较强大的分布式存储,但是它整个系统非常复杂需要大量的人力进行维护,和探探的产品不是很符合,所以暂时不考虑。

    GlusterFS 为本身是一个非常成熟的对象存储的方案。2011年左右被收购了,他们原版人马又做了另外一个存储系统MINIO,仙鹤就是他们的 logo。

    MINIO 的文档非常详细、具体,再加上他们之前在存储方面有十几年的经验,所以就这样打动了探探团队,作为选型的标的。

    MinIO 介绍

    Minio 是个基于 Golang 编写的开源对象存储套件,虽然轻量,却拥有着不错的性能。

    官网地址:MinIO | High Performance, Kubernetes Native Object Storage

    何为对象存储?

    对象存储服务(Object Storage Service,OSS)是一种海量、安全、低成本、高可靠的云存储服务,适合存放任意类型的文件。容量和处理能力弹性扩展,多种存储类型供选择,全面优化存储成本。

    MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。

    它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。

    MinIO是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或者 MySQL。

    对于中小型企业,如果不选择存储上云,那么 Minio 是个不错的选择,麻雀虽小,五脏俱全。

    当然 Minio 除了直接作为对象存储使用,还可以作为云上对象存储服务的网关层,无缝对接到 Amazon S3、MicroSoft Azure。

    MINIO 基础概念

    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尽可能分布在不同的节点上

    Set /Drive 的关系

    Set /Drive 这两个概念是 MINIO 里面最重要的两个概念,一个对象最终是存储在 Set 上面的。

    我们来看下边 MINIO 集群存储示意图,每一行是一个节点机器,这有 32 个节点,每个节点里有一个小方块我们称之 Drive,Drive 可以简单地理解为一个硬盘。

    图中,一个节点有 32 个 Drive,相当于 32 块硬盘。

    Set 是另外一个概念,Set 是一组 Drive 的集合,图中,所有蓝色、橙色背景的Drive(硬盘)的就组成了一个 Set.

    在这里插入图片描述

    MIINO如何写入对象?

    MINIO 是通过数据编码,将原来的数据编码成 N 份,N 就是一个 Set 上面 Drive 的数量,后面多次提到的 N 都是指这个意思。

    上图中,一个 Set 上面 Drive 的数量,是3.

    对象被编码成N份之后,把每一份,写到对应的 Drive 上面,这就是把一个对象存储在整个 Set 上。

    在这里插入图片描述

    一个集群包含多个 Set,每个对象最终存储在哪个 Set 上是根据对象的名称进行哈希,然后影射到唯一的 Set 上面,这个方式从理论上保证数据可以均匀的分布到所有的 Set 上。

    根据的观测,数据分布的也非常均匀,一个 Set 上包含多少个 Drive 是由系统自动根据集群规模算出来的,当然,也可以自己去配置。

    一个 Set 的 Drive 系统会考虑尽可能把它放在多的节点上面,保证它的可靠性。

    Minio存储架构

    Minio针对不同应用场景也设置了对应的存储架构:

    单主机,单硬盘模式

    img

    该模式下,Minio只在一台服务器上搭建服务,且数据都存在单块磁盘上,该模式存在单点风险,主要用作开发、测试等使用

    启动的命令为:

    minio --config-dir ~/tenant1 server --address :9001 /disk1/data/tenant1
    

    单主机,多硬盘模式

    img

    该模式下,Minio在一台服务器上搭建服务,但数据分散在多块(大于4块)磁盘上,提供了数据上的安全保障

    minio --config-dir ~/tenant1 server --address :9001 /disk1/data/tenant1 /disk2/data/tenant1 /disk3/data/tenant1 /disk4/data/enant1
    

    多主机、多硬盘模式(分布式)

    img

    该模式是Minio服务最常用的架构,通过共享一个access_key和secret_key,在多台(2-32)服务器上搭建服务,且数据分散在多块(大于4块,无上限)磁盘上,提供了较为强大的数据冗余机制(Reed-Solomon纠删码)。

    export MINIO_ACCESS_KEY=<TENANT1_ACCESS_KEY>
    export MINIO_SECRET_KEY=<TENANT1_SECRET_KEY>
    minio --config-dir ~/tenant1 server --address :9001 http://192.168.10.11/data/tenant1 http://192.168.10.12/data/tenant1 http://192.168.10.13/data/tenant1 http://192.168.10.14/data/tenant1
    

    分布式Minio有什么好处?

    在大数据领域,通常的设计理念都是无中心和分布式。Minio分布式模式可以帮助你搭建一个高可用的对象存储服务,你可以使用这些存储设备,而不用考虑其真实物理位置。

    数据保护

    分布式Minio采用 纠删码来防范多个节点宕机和位衰减bit rot

    分布式Minio至少需要4个硬盘,使用分布式Minio自动引入了纠删码功能。

    高可用

    单机Minio服务存在单点故障,相反,如果是一个有N块硬盘的分布式Minio, 只要有N/2硬盘在线,你的数据就是安全的。

    不过你需要至少有N/2+1个硬盘来创建新的对象。

    例如,一个16节点的Minio集群,每个节点16块硬盘,就算8台服務器宕机,这个集群仍然是可读的,不过你需要9台服務器才能写数据。

    注意,只要遵守分布式Minio的限制,你可以组合不同的节点和每个节点几块硬盘。

    比如,你可以使用2个节点,每个节点4块硬盘,也可以使用4个节点,每个节点两块硬盘,诸如此类。

    一致性

    Minio在分布式和单机模式下,所有读写操作都严格遵守read-after-write一致性模型。

    MinIO的数据高可靠

    Minio使用了Erasure Code 纠删码Bit Rot Protection 数据腐化保护这两个特性,所以MinIO的数据可靠性做的高。

    Erasure Code纠删码

    纠删码(Erasure Code)简称EC,是一种数据保护方法,它将数据分割成片段,把冗余数据块扩展、编码,并将其存储在不同的位置,比如磁盘、存储节点或者其它地理位置。

    从数据函数角度来说,纠删码提供的保护可以用下面这个简单的公式来表示:n = k + m。变量“k”代表原始数据或符号的值。变量“m”代表故障后添加的提供保护的额外或冗余符号的值。变量“n”代表纠删码过程后创建的符号的总值。

    举个例子,假设n=16,代表有16块磁盘,另外,有10份原始文件一模一样,称为k,16 = 10 +m,这个m就是可以恢复的校验块个数,所以m是6,任意6个不可用,原始文件都可以恢复,极端情况,10个原始文件坏掉6个,靠4个原始的加上6个校验块,可以把坏掉的6个原始文件恢复,这个用到数学行列式矩阵知识,不做展开。

    MinIO的编码方式,将一个对象编码成若干个数据块和校验块,我们简称为Erasure Code码,这个是编码的类型,这种编码的类型,还需要算法来实现,minio 采用的是 Reed-Solomon算法。

    MinIO使用Reed-Solomon算法,该算法把对象编码成若干个数据块和校验块。

    Reed-Solomon算法的特点:

    • 低冗余
    • 高可靠

    为了表述方便,把数据块和校验块统称为编码块,之后我们可以通过编码块的一部分就能还原出整个对象。

    Reed-Solomon code

    Reed-Solomon 是纠删码的实现算法的一种,当然,也是一种恢复丢失和损坏数据的数学算法,

    Minio默认采用Reed-Solomon code将数据拆分成N/2个数据块和N/2个奇偶校验块。

    这就意味着如果是16块盘,一个对象会被分成8个数据块、8个奇偶校验块,你可以丢失任意8块盘(不管其是存放的数据块还是校验块),你仍可以从剩下的盘中的数据进行恢复。

    img

    如上图,如我们所知,一个对象存储在一个Set上面,这个Set包含16个Drive,其中灰色的一半是数据库,橙色的一半是校验块,这种方式最多能忍受一半的编码丢失或损坏。

    所有编码块的大小是原对象的2倍,跟传统多副本存储方案相比,他只冗余存了一份,但可靠性更高。

    纠删码的工作原理和RAID或者副本不同,像RAID6只能在损失两块盘,或者以下的情况下不丢数据,而Minio纠删码可以在丢失一半的盘的情况下,仍可以保证数据安全。

    而且Minio纠删码是作用在对象级别,可以一次恢复一个对象,而RAID是作用在卷级别,数据恢复时间很长。

    Minio对每个对象单独编码,存储服务一经部署,通常情况下是不需要更换硬盘或者修复。

    此外,针对不同应用所需的数据安全级别不同,Minio还提供了存储级别(Storage Class)的配置,调整数据块和校验块的比例,做到对空间的最佳使用。

    img

    比如在将比例调整为14:2后,存储100M的数据占用的空间仅为114M。

    Bit Rot Protection:

    接下来讲Bit Rot Protection,直译就是腐烂。

    它只是物理设备上的一些文件细微的损坏,还没有被操作系统所硬件所察觉,但是他已经损坏了。

    Bit Rot 位衰减又被称为数据腐化Data Rot无声数据损坏Silent Data Corruption,

    位衰减可以理解为无形中的数据丢失——或者称为“Bit rot”, 是指物理存储介质的衰减所带来的隐患将凸显出来。

    位衰减是目前硬盘数据的一种严重数据丢失问题。

    硬盘上的数据可能会神不知鬼不觉就损坏了,也没有什么错误日志。

    一项对150万块硬盘的研究表明,每90块硬盘就有1块有这种“软错误”,这个错误不但会导致数据丢失,还会导致RAID错误。

    针对这一问题,最新的Minio采用了HighwayHash算法计算校验和来防范位衰减,根据测试结果,其可以实现10GB/s的处理速度。

    大致的做法是:

    MinIO把之前的编码块进行 HighwayHash 编码,最后要校验这个编码,以确保每个编码是正确的。

    文件的修复

    另外,MinIO提供了一个管理工具,可以对所有编码块进行校验,如果发现编码块有问题,再去修复它。

    得益于Reed-Solomon纠删码,Minio可以更加灵活的对文件进行修复。

    目前,Minio提供了全量、bucket、文件夹、文件等各个粒度的修复操作:

    递归修复

    
    $ mc admin heal -r myminio
    
    

    指定桶修复

    $ mc admin heal -r myminio/dev
    

    下面是几个例子:

    img

    img

    img

    img

    相比一般的RAID方式,Minio可以在非常小的粒度下对文件进行修复操作,灵活性有了很大提高。

    修复后,可以JSON格式列出指定路径(文件、大小)

    
    $ mc ls -r --json myminio/dev
    
    {
     "status": "success",
     "type": "file",
     "lastModified": "2019-05-30T09:43:07.763-04:00",
     "size": 44996289,
     "key": "myFile",
     "etag": "bae58dc18zzzzz54c14e233b520e0a"
    }
    
    

    部署实操

    Minio 提供了两种部署方式:

    • 单机部署
    • 分布式

    两种部署方式都非常简单,其中分布式部署还提供了纠删码功能来降低数据丢失的风险。

    单机部署:

    minio server /data
    

    分布式部署:

    export MINIO_ACCESS_KEY=<ACCESS_KEY>
    export MINIO_SECRET_KEY=<SECRET_KEY>
    minio server http://host{1...n}/export{1...m} http://host{1...o}/export{1...m}
    

    Docker Compose部署

    官方的文档:

    https://docs.min.io/cn/deploy-minio-on-docker-compose.html

    Docker Compose允许定义和运行单主机,多容器Docker应用程序。

    使用Compose,您可以使用Compose文件来配置MinIO服务。 然后,使用单个命令,您可以通过你的配置创建并启动所有分布式MinIO实例。 分布式MinIO实例将部署在同一主机上的多个容器中。 这是建立基于分布式MinIO的开发,测试和分期环境的好方法。

    环境说明

    操作系统 ip地址 docker版本 Docker Compose版本
    centos 7.6 192.168.31.34 19.03.8 1.24.1

    运行分布式MinIO

    在Docker Compose上部署分布式MinIO,请下载docker-compose.yaml到你的当前工作目录。Docker Compose会pull MinIO Docker Image,所以你不需要手动去下载MinIO binary。然后运行下面的命令

    docker-compose pull
    docker-compose up -d
    

    现在每个实例都可以访问,端口从9001到9004,请在浏览器中访问http://127.0.0.1:9001/

    注意事项

    • 默认情况下Docker Compose file使用的是最新版的MinIO server的Docker镜像,你可以修改image tag来拉取指定版本的MinIO Docker image.

    • 默认情况下会创建4个minio实例,你可以添加更多的MinIO服务(最多总共16个)到你的MinIO Comose deployment。添加一个服务

      • 复制服务定义并适当地更改新服务的名称。
      • 更新每个服务中的命令部分。
      • 更新要为新服务公开的端口号。 另外,请确保分配给新服务的端口尚未使用。

      关于分布式MinIO的更多资料,请访问这里.

    • Docker compose file中的MinIO服务使用的端口是9001到9004,这允许多个服务在主机上运行。

    访问MinIO

    http://192.168.31.34:9001/minio/
    

    用户名:minio,密码:minio123

    img

    登录之后,效果如下:

    img

    MinIO操作

    修改密码

    点击右上角

    img

    提示:无法通过浏览器更新此用户的凭据

    img

    注意:如果需要修改密码,修改docker-compose.yaml中的MINIO_ACCESS_KEY和MINIO_SECRET_KEY变量即可。

    创建bucket(文件夹)

    先来创建一个bucket

    img

    创建data

    img

    上传文件

    选中data,点击右下角的上传文件

    img

    选择一张图片,上传成功之后,效果如下:

    img

    查看bucket文件信息

    点击data,查看与设置该Object的基本信息:
    查看共享地址Shareable Link

    img

    设置到期时间,最大可保存时间为7天

    对话框上方弹出该Object现剩余到期时间

    img

    附录1:Minio纠删码的补充学习

    Minio使用纠删码erasure code和校验和checksum来保护数据免受硬件故障和无声数据损坏。 即便您丢失一半数量(N/2)的硬盘,您仍然可以恢复数据。

    什么是纠删码erasure code?

    纠删码是一种恢复丢失和损坏数据的数学算法, Minio采用Reed-Solomon code 算法将对象拆分成N/2数据和N/2 奇偶校验块。 这就意味着如果是12块盘,一个对象会被分成6个数据块、6个奇偶校验块,你可以丢失任意6块盘(不管其是存放的数据块还是奇偶校验块),你仍可以从剩下的盘中的数据进行恢复,是不是很NB,感兴趣的同学请FQgoogle。

    为什么纠删码有用?

    纠删码的工作原理和RAID或者复制不同,像RAID6可以在损失两块盘的情况下不丢数据,而Minio纠删码可以在丢失一半的盘的情况下,仍可以保证数据安全。 而且Minio纠删码是作用在对象级别,可以一次恢复一个对象,而RAID是作用在卷级别,数据恢复时间很长。 Minio对每个对象单独编码,存储服务一经部署,通常情况下是不需要更换硬盘或者修复。Minio纠删码的设计目标是为了性能和尽可能的使用硬件加速。

    Erasure

    Minio纠删码实验

    1. 前提条件:

    安装Minio- Minio快速入门

    2. 以纠删码模式运行Minio

    示例: 使用Minio,在12个盘中启动Minio服务。

    minio server /data1 /data2 /data3 /data4 /data5 /data6 /data7 /data8 /data9 /data10 /data11 /data12
    

    示例: 使用Minio Docker镜像,在8块盘中启动Minio服务。

    docker run -p 9000:9000 --name minio 
      -v /mnt/data1:/data1 
      -v /mnt/data2:/data2 
      -v /mnt/data3:/data3 
      -v /mnt/data4:/data4 
      -v /mnt/data5:/data5 
      -v /mnt/data6:/data6 
      -v /mnt/data7:/data7 
      -v /mnt/data8:/data8 
      minio/minio server /data1 /data2 /data3 /data4 /data5 /data6 /data7 /data8
    

    3. 验证是否设置成功

    你可以随意拔掉硬盘,看Minio是否可以正常读写。

    附录2:什么是位衰减bit rot保护?

    位衰减又被称为数据腐化Data Rot、无声数据损坏Silent Data Corruption,是目前硬盘数据的一种严重数据丢失问题。

    硬盘上的数据可能会神不知鬼不觉就损坏了,也没有什么错误日志。

    正所谓明枪易躲,暗箭难防,这种背地里犯的错比硬盘直接咔咔宕了还危险。

    不过不用怕,Minio纠删码采用了高速 HighwayHash 基于哈希的校验和来防范位衰减。

    附录3:Springboot集成MinIO基础实现

    新建一个项目springboot-minio,主要代码:

    @RestController
    @Slf4j
    public class FileController {
    
        @Autowired
        private MinioClient minioClient;
        @Value("${minio.bucketName}")
        private String bucketName;
    
        @GetMapping("/list")
        public List<Object> list() throws Exception {
            //获取bucket列表
            Iterable<Result<Item>> myObjects = minioClient.listObjects(
                    ListObjectsArgs.builder().bucket(bucketName).build());
            Iterator<Result<Item>> iterator = myObjects.iterator();
            List<Object> items = new ArrayList<>();
            String format = "{'fileName':'%s','fileSize':'%s'}";
            while (iterator.hasNext()) {
                Item item = iterator.next().get();
                items.add(JSON.parse(String.format(format, item.objectName(),
                        formatFileSize(item.size()))));
            }
            return items;
        }
    
        @PostMapping("/upload")
        public ResultBean upload(@RequestParam(name = "file", required = false)
                                  MultipartFile[] file) {
            ResultBean resultBean = ResultBean.newInstance();
            if (file == null || file.length == 0) {
                return resultBean.error("上传文件不能为空");
            }
            List<String> orgfileNameList = new ArrayList<>(file.length);
            for (MultipartFile multipartFile : file) {
                String orgfileName = multipartFile.getOriginalFilename();
                orgfileNameList.add(orgfileName);
                try {
                    //文件上传
                    InputStream in = multipartFile.getInputStream();
                    minioClient.putObject(
                            PutObjectArgs.builder().bucket(bucketName).object(orgfileName).stream(
                                    in, multipartFile.getSize(), -1)
                                    .contentType(multipartFile.getContentType())
                                    .build());
                    in.close();
                } catch (Exception e) {
                    log.error(e.getMessage());
                    return resultBean.error("上传失败");
                }
            }
            Map<String, Object> data = new HashMap<String, Object>();
            data.put("bucketName", bucketName);
            data.put("fileName", orgfileNameList);
            return resultBean.ok("上传成功", data);
        }
    
        @RequestMapping("/download/{fileName}")
        public void download(HttpServletResponse response, @PathVariable("fileName")
                String fileName) {
            InputStream in = null;
            try {
                // 获取对象信息
                StatObjectResponse stat = minioClient.statObject(
                        StatObjectArgs.builder().bucket(bucketName).object(fileName).build());
                response.setContentType(stat.contentType());
                response.setHeader("Content-Disposition", "attachment;filename=" +
                        URLEncoder.encode(fileName, "UTF-8"));
                //文件下载
                in = minioClient.getObject(
                        GetObjectArgs.builder()
                                .bucket(bucketName)
                                .object(fileName)
                                .build());
                IOUtils.copy(in, response.getOutputStream());
            } catch (Exception e) {
                log.error(e.getMessage());
            } finally {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        log.error(e.getMessage());
                    }
                }
            }
        }
    
        @DeleteMapping("/delete/{fileName}")
        public ResultBean delete(@PathVariable("fileName") String fileName) {
            ResultBean resultBean = ResultBean.newInstance();
            try {
                minioClient.removeObject(
                        RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
            } catch (Exception e) {
                log.error(e.getMessage());
                return resultBean.error("删除失败");
            }
            return resultBean.ok("删除成功", null);
        }
    
        private static String formatFileSize(long fileS) {
            DecimalFormat df = new DecimalFormat("#.00");
            String fileSizeString = "";
            String wrongSize = "0B";
            if (fileS == 0) {
                return wrongSize;
            }
            if (fileS < 1024) {
                fileSizeString = df.format((double) fileS) + " B";
            } else if (fileS < 1048576) {
                fileSizeString = df.format((double) fileS / 1024) + " KB";
            } else if (fileS < 1073741824) {
                fileSizeString = df.format((double) fileS / 1048576) + " MB";
            } else {
                fileSizeString = df.format((double) fileS / 1073741824) + " GB";
            }
            return fileSizeString;
        }
    
    }
    

    ​ yml配置

    minio:
      endpoint: http://10.12.105.15:9000
      accesskey: minioadmin
      secretKey: minioadmin
      bucketName: chenxin-bucket
    

    ​ 启动Springboot项目后,调用localhost:8080/list

    img

    ​ 用postman上传试下,很明显上传成功了。

    img

    同理下载也是一样,看下接口就清楚了,接口后面跟着文件名就可以下载了,当然这个

    后面要做成存储到数据库里,这个后面我拓展出来,先会使用再说。

    参考代码:https://gitee.com/cx_gitee/springboot-minio

    参考文档

    https://www.jianshu.com/p/95538d262b93

    http://tech.dianwoda.com/2018/12/04/fen-bu-shi-cun-chu-xi-tong-miniojian-jie/

    https://www.jianshu.com/p/68ac0477291d

    https://studygolang.com/articles/34762

    http://docs.minio.org.cn/docs/master/distributed-minio-quickstart-guide

    https://blog.csdn.net/qq_31821733/article/details/119882573

    https://blog.51cto.com/zyrs/2749314

    https://docs.min.io/cn/deploy-minio-on-docker-compose.html

  • 相关阅读:
    uva11025 The broken pedometer
    uva131 The Psychic Poker Player
    子集生成算法
    uva10167 Birthday Cake
    poj1129 Channel Allocation
    poj2676 Sudoku
    Emacs杂谈(一)Emacs环境 c++ 快捷键
    poj1416 Shredding Company
    英文报刊推荐
    搜索练习(二)工作效益
  • 原文地址:https://www.cnblogs.com/crazymakercircle/p/15408581.html
Copyright © 2020-2023  润新知