it编程 > 编程语言 > 其他编程

MinIO学习指南看这一篇就够了

20人参与 2025-02-26 其他编程

一.前言

对象存储是一种数据存储架构,设计用于管理和处理大量非结构化数据。与传统的文件存储和块存储不同,对象存储通过将数据分解为离散的、独立的单元或“对象”来存储每个对象包含数据本身、相关的元数据和一个唯一的标识符。

官网:https://www.minio.org.cn

以下是对象存储、服务器磁盘和分布式文件系统的对比表格:

特性对象存储服务器磁盘分布式文件系统
存储方式以对象为基本单位存储数据,每个对象包含数据、元数据和唯一标识符数据直接存储在服务器的本地磁盘上数据分布在多个服务器节点上,通过网络进行数据访问
优点高可扩展性:能够轻松扩展至数十乃至数百eb的容量。 高效性:扁平化结构,不受复杂目录系统对性能的影响。 安全性高:通常凭借http调用对象存储本身提供的认证密钥来提供数据访问。 访问方便:支持http(s)协议,采用rest的api方式调用和检索数据。 成本相对低:与块存储方式相比,对象存储是最具成本效益的数据存储类型。开发便捷:直接使用服务器磁盘,无需复杂的配置和开发工作。 成本低:如果已有服务器和磁盘,不需要额外支付存储服务费用。容易实现扩容:可以通过增加更多的服务器节点来扩展存储容量,实现水平扩展。
缺点最终一致性:由于不同节点的位置不同,数据同步时可能会有一定时间的延迟或者错误。 不适合频繁变动的数据:对象存储比较适合存储那些变动不大甚至不变的文件。扩展困难:服务器磁盘的容量有限,当存储空间不足时,扩展磁盘容量可能需要更换更大容量的硬盘或者增加额外的磁盘阵列,成本较高且操作复杂。复杂度高:分布式文件系统的部署和维护相对复杂,需要专业的运维团队来管理。

分布式文件系统

分布式文件系统(distributed file system, dfs)是一种文件系统,它使文件可以跨越多个服务器或存储设备存储和访问。dfs 通过网络将多个存储资源组合成一个统一的文件系统,使用户和应用程序可以像访问本地文件一样透明地访问远程文件。

分布式文件系统的关键特性:

minio

minio 是一个高性能、轻量级对象存储服务器,专为大规模数据存储和分析而设计。它兼容 amazon s3 api,可以无缝替代 amazon s3 作为存储后端,并且支持在各种环境中部署,包括物理服务器、虚拟机、容器等。

fastdfs和minio的区别

以下是minio和fastdfs的对比表格:

特性miniofastdfs
存储类型对象存储文件存储
架构单一守护进程,支持分布式tracker-storage 分离
协议支持s3 兼容,支持 http/rest api专有协议
数据冗余纠删码,多节点和磁盘故障容错主从复制,多副本
性能高性能,适合大文件和海量数据存储适合小文件存储,上传/下载性能较高
扩展性高度可扩展,支持水平扩展可扩展但管理复杂
管理工具web 界面,支持 prometheus 监控命令行工具
生态系统广泛,集成度高生态相对较小,集成度低
安装部署简单,开箱即用复杂,需要专业知识
社区与支持活跃,有官方文档和社区支持缺乏官方文档和持续更新

minio和fastdfs各有优势,minio在兼容性、扩展性、性能和生态系统方面表现更佳,适合云原生应用、大数据分析等场景;而fastdfs在处理小文件方面性能出色,适合需要海量小文件存储的场景。具体选择可以根据实际需求和使用场景来决定。

二.windows环境安装minio

1、下载服务端和客户端安装包文件

下载地址:minio | code and downloads to create high performance object storage
服务端文件:minio.exe 用于接收文件信息
客户端文件:mac.exe 用于上传文件 ,如果用程序代码操作文件存储,只启动服务端就ok

2、启动minio服务器

特别提示:在windows 安装软件我们都习惯双击.exe 文件启动。minio可不行奥,可不行,可不行。千万不能去双击运行,这样可能会导致最终启动失败;无论是windows还是linux都建议通过命令启动的。

以管理员权限打开cmd窗口,进入到minio.exe所在bin目录

2.2、设置用户名

用于登录minio客户端

setx minio_root_user name 

设置登录密码

 setx minio_root_password password

2.3、启动minio服务

.\minio.exe server d:\develpo\minio\data --console-address "127.0.0.1:9000" --address "127.0.0.1:9005"

d:\develpo\minio\data 指定数据存放路径

9005是控制台端口,9000是服务的端口。

4.5、访问minio服务器

访问客户端地址 http://127.0.0.1:9000/ 输入用户密码

使用 docker 安装

docker run -p 9000:9000 minio/minio server /data

三. 基本概念

基本概念

概念定义特点
object存储到minio的基本对象,如文件、字节流等具有唯一标识,可设置元数据
bucket用来存储object的逻辑空间,相当于顶层文件夹数据隔离,命名唯一,可设置权限
drive存储数据的磁盘,启动时以参数传入是数据存储载体,容量有限
set一组drive的集合分布式部署自动划分,对象存储于其上,drive数量固定,尽可能分布在不同节点

minio 纠删码 ec (erasure code)

纠删码(erasure code, ec) 是一种数据保护方法,它将数据分割成片段,生成冗余数据块,并将这些数据块存储在不同的位置,如磁盘、存储节点或其他地理位置。minio 采用 reed-solomon 纠删码实现,将对象拆分成数据块和奇偶校验块,以提高数据的冗余性和可用性。

简单来说就是可以通过数学计算,把丢失的数据进行还原,它可以将n份原始数据,增加m份数据,并能通过n+m份中的任意n份数据,还原为原始数据。
即如果有任意小于等于m份的数据失效,仍然能通过剩下的数据还原出来。

举个最简单例子就是有两个数据(d1, d2),用一个校验和y(d1 + d2 = y)即可保证即使丢失其中一个,依然可以还原数据。如丢失 d1 ,则使用 y - d2 = d1 还原,同理,d2 丢失或者y丢失,均可通过计算得出。

存储形式

当文件对象上传到minio时,数据会以特定的形式存储在对应的数据存储磁盘中。具体存储形式如下:

目录结构

数据块和元数据文件

例如,假设我们有四块磁盘:data01、data02、data03、data04。存储形式如下:

data01

data02

data03

data04

这种存储形式确保了数据的冗余和高可用性,通过纠删码技术,即使部分磁盘损坏,数据仍然可以被恢复。

minio中的存储级别

minio当前支持两种存储级别:reduced redundancystandard,通过对两种级别的设置来修改对象的parity drives ( p)(奇偶校验块)和data drives (d)(数据块)的比例,让用户能够更好的控制磁盘使用率和容错性。

standard

standard存储级别包含比reduced_redundancy存储级别更多的奇偶校验块,因此standard存储级别的奇偶校验块需要满足如下条件:

standard存储级别的奇偶校验块的默认值取决于erasure set中的磁盘数量:

erasure set sizedefault parity (ec:n)
5 or fewerec:2
6-7ec:3
8 or moreec:4

reduced_redundancy存储级别包含比standard存储级别更少的奇偶校验块,因此reduced_redundancy存储级别的奇偶校验块需要满足如下条件:

reduced_redundancy存储级别的奇偶校验块默认值为:ec:2

使用

配置存储级别
有以下两种配置方式:

环境变量

export minio_storage_class_standard=ec:4
export minio_storage_class_rrs=ec:2

设置以上环境变量并重启服务

mc admin

mc admin config set myminio/ storage_class standard="ec:4"
mc admin config set myminio/ storage_class rrs="ec:2"
# 重启minio服务
mc admin service restart myminio/

存储方案

四. 命令操作

列出存储桶

mc ls <alias>

这会列出指定 minio 服务器上的所有存储桶。

创建存储桶

mc mb <alias>/<bucket_name>

这会在指定 minio 服务器上创建一个新的存储桶。

上传文件

mc cp <file_path> <alias>/<bucket_name>

这会将本地文件上传到指定的 minio 存储桶中。

下载文件

mc cp <alias>/<bucket_name>/<file_name> <local_file_path>

这会将 minio 存储桶中的文件下载到本地。

复制对象

mc cp <source> <target>

这会复制对象从一个位置到另一个位置,可以是存储桶内的对象或不同存储桶间的对象。

移动对象

mc mv <source> <target>

这会移动对象从一个位置到另一个位置,与复制不同的是,移动后源位置的对象将被删除。

删除对象

mc rm <alias>/<bucket_name>/<object_name>

这会删除指定的对象。

删除存储桶

mc rb <alias>/<bucket_name>

这会删除指定的存储桶及其中的所有对象。

五 . minio 整合 springboot

1、导入依赖:

 <dependency>
       <groupid>io.minio</groupid>
       <artifactid>minio</artifactid>
       <version>8.2.2</version>
 </dependency>

2、在springboot的配置文件中编写 minio 的配置:

minio:
  config:
    url: http://127.0.0.1:9005 #ip地址
    accesskey: admin #  账号
    secretkey: admin962464 #  密码
    secure: false #如果是true,则用的是https而不是http,默认值是true
    bucketname: "test"  # 桶的名字 相当于文件夹

3、编写 minio 配置类:

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;
@data
@configuration
@configurationproperties(prefix = "minio.config")
public class minioconfig {
    /**
     * 服务地址
     */
    private string url;
    /**
     * 用户名
     */
    private string accesskey;
    /**
     * 密码
     */
    private string secretkey;
    /**
     * 存储桶名称
     */
    private string bucketname;
    @bean
    public minioclient getminioclient() {
        return minioclient.builder().endpoint(url).credentials(accesskey, secretkey).build();
    }
}

4、编写 minio 的工具类:

import com.jjy.shopping_file_service.config.minioconfig;
import io.minio.*;
import io.minio.http.method;
import io.minio.messages.bucket;
import io.minio.messages.item;
import lombok.allargsconstructor;
import lombok.extern.slf4j.slf4j;
import org.springframework.stereotype.component;
import org.springframework.util.fastbytearrayoutputstream;
import org.springframework.util.stringutils;
import org.springframework.web.multipart.multipartfile;
import javax.annotation.resource;
import javax.servlet.servletoutputstream;
import javax.servlet.http.httpservletresponse;
import java.text.simpledateformat;
import java.util.arraylist;
import java.util.date;
import java.util.list;
import java.util.uuid;
@slf4j
@component
public class minioutil {
    @resource
    private minioconfig minioconfig;
    @resource
    private minioclient minioclient;
    /**
     * 查看存储bucket是否存在
     *
     * @param bucketname 存储桶名称
     * @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
     *
     * @param bucketname 存储桶名称
     * @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 存储桶名称
     * @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
     *
     * @return 存储桶列表
     */
    public list<bucket> getallbuckets() {
        try {
            return minioclient.listbuckets();
        } catch (exception e) {
            e.printstacktrace();
        }
        return null;
    }
    /**
     * 文件上传
     *
     * @param file 文件
     * @return 文件对象名称
     */
    public string upload(multipartfile file) {
        string originalfilename = file.getoriginalfilename();
        system.out.println(originalfilename);
        if (!stringutils.hastext(originalfilename)) {
            throw new runtimeexception();
        }
        string filename = uuid.randomuuid() + originalfilename.substring(originalfilename.lastindexof("."));
        string prefix = new simpledateformat("yyyy/mm/dd").format(new date());
        string objectname = prefix + "/" + filename;
        try {
            putobjectargs objectargs = putobjectargs.builder().bucket(minioconfig.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 = getpresignedobjecturlargs
                .builder()
                .bucket(minioconfig.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
     */
    public void download(string filename, httpservletresponse res) {
        getobjectargs objectargs = getobjectargs.builder().bucket(minioconfig.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.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(minioconfig.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 是否删除成功
     */
    public boolean remove(string filename) {
        try {
            minioclient.removeobject(removeobjectargs.builder()
                    .bucket(minioconfig.getbucketname())
                    .object(filename)
                    .build());
        } catch (exception e) {
            return false;
        }
        return true;
    }
}

5、controller:

import com.cyw.miniodemo.config.minioconfig;
import com.cyw.miniodemo.pojo.rst;
import com.cyw.miniodemo.service.fileuploadservice;
import com.cyw.miniodemo.utils.minioutil;
import lombok.allargsconstructor;
import lombok.extern.slf4j.slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.multipartfile;
import javax.servlet.http.httpservletresponse;
import java.util.hashmap;
import java.util.map;
@slf4j
@restcontroller
@requestmapping("/api/file")
@allargsconstructor
public class fileuploadcontroller {
    private minioconfig minioconfig;
    private minioutil minioutil;
    private fileuploadservice fileuploadservice;
    @getmapping("/bucketexists")
    public rst bucketexists(@requestparam("bucketname") string bucketname) {
        map<string, object> map = new hashmap<>();
        map.put("bucketexists", minioutil.bucketexists(bucketname));
        return rst.ok("查询成功", map);
    }
    @getmapping("/makebucket")
    public rst makebucket(@requestparam("bucketname") string bucketname) {
        map<string, object> map = new hashmap<>();
        map.put("makebucketsuccess", minioutil.makebucket(bucketname));
        return rst.ok("创建成功", map);
    }
    @getmapping("/removebucket")
    public rst removebucket(@requestparam("bucketname") string bucketname) {
        map<string, object> map = new hashmap<>();
        map.put("deletebucketsuccess", minioutil.removebucket(bucketname));
        return rst.ok("删除成功", map);
    }
    @getmapping("/getallbuckets")
    public rst getallbuckets() {
        map<string, object> map = new hashmap<>();
        map.put("buckets", minioutil.getallbuckets());
        return rst.ok("查询成功", map);
    }
    @postmapping("/upload")
    public rst upload(@requestparam("file") multipartfile file) {
        string objectname = minioutil.upload(file);
        if (objectname != null) {
            map<string, object> map = new hashmap<>();
            map.put("url", (minioconfig.getendpoint() + "/" + minioconfig.getbucketname() + "/" + objectname));
            return rst.ok("上传成功", map);
        }
        return rst.fail("上传失败");
    }
    @getmapping("/preview")
    public rst preview(@requestparam("filename") string filename) {
        map<string, object> map = new hashmap<>();
        map.put("url", minioutil.preview(filename));
        return rst.ok("预览成功", map);
    }
    @getmapping("/download")
    public rst download(@requestparam("filename") string filename, httpservletresponse resp) {
        minioutil.download(filename, resp);
        return rst.ok();
    }
    @postmapping("/delete")
    public rst remove(@requestbody map<string, string> params) {
        string url = params.get("url");
        string objname = url.substring(url.lastindexof(minioconfig.getbucketname() + "/") + minioconfig.getbucketname().length() + 1);
        log.info("删除对象: {}", objname);
        minioutil.remove(objname);
        return rst.ok("删除成功");
    }
}

到此这篇关于minio学习指南看这一篇就够了的文章就介绍到这了,更多相关minio命令内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)
打赏 微信扫一扫 微信扫一扫

您想发表意见!!点此发布评论

推荐阅读

centos部署open-webui的完整流程记录

02-20

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

02-20

Git忽略文件.gitignore操作方法指南

02-20

Flink结合Kafka实现通用流式数据处理

03-08

Git中恢复已删除分支的几种方法

02-13

关于rpc长连接与短连接的思考记录

02-13

猜你喜欢

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论