MinIO的搭建与学习


MinIO介绍

MinIO 是一种构建高性能的云原生对象存储,基于机器学习,大数据分析,海量存储的架构进行设计,单个对象存储数据最大可达5TB,适合存储海量图片、视频、日志文件、备份数据和容器/虚拟机镜像等。MinIO主要采用Golang语言实现,基于Apache License v2.0开源协议,虽然轻量,却拥有着不错的性能,客户端与存储服务器之间采用http/https通信协议。其优点如下:

  • 高性能
    MinIO 是全球领先的对象存储先锋,目前在全世界有数百万的用户. 在标准硬件上,读/写速度上高达183 GB / 秒 和 171 GB / 秒。
  • 可扩展性
    MinIO利用了Web缩放器的来之不易的知识,为对象存储带来了简单的缩放模型。 这是我们坚定的理念 “简单可扩展.” 在 MinIO, 扩展从单个群集开始,该群集可以与其他MinIO群集联合以创建全局名称空间, 并在需要时可以跨越多个不同的数据中心。 通过添加更多集群可以扩展名称空间, 更多机架,直到实现目标。
  • 云的原生支持
    MinIO 是在过去4年的时间内从0开始打造的一款软件 ,符合一切原生云计算的架构和构建过程,并且包含最新的云计算的全新的技术和概念。
  • 开放全部源代码 + 企业级支持
    MinIO 基于Apache V2 license 100% 开放源代码 。 这就意味着 MinIO的客户能够自动的、无限制、自由免费使用和集成MinIO、自由的创新和创造、 自由的去修改、自由的再次发行新的版本和软件。
  • 与Amazon S3 兼容
    亚马逊云的 S3 API(接口协议) 是在全球范围内达到共识的对象存储的协议,是全世界内大家都认可的标准。 MinIO 在很早的时候就采用了 S3 兼容协议,并且MinIO 是第一个支持 S3 Select 的产品。
  • 简单
    极简主义是MinIO的指导性设计原则。简单性减少了出错的机会,提高了正常运行时间,提供了可靠性,同时简单性又是性能的基础。 只需下载一个二进制文件然后执行,即可在几分钟内安装和配置MinIO。

Windows安装使用MinIO

1、进入官网(https://min.io/download#/windows)进行下载

2、下载完成后是一个exe文件,新建一个目录用于存放数据

3、打开cmd窗口,切换到minio.exe目录下,运行如下命令
注意:该exe文件不能双击运行

minio.exe server ./MinIOData  // ./MinIOData是存放数据的文件夹,需要自己指定

运行成功如下图

4、浏览器输入http://localhost:9000进行登录页面
输入网址http://localhost:9000,浏览器将自动跳转到http://localhost:9224/login

5、输入账号密码进行登录
系统默认登录用户名和密码都是 minioadmin,登录成功后进入系统主页

6、点击Create a Bucket创建一个目录进行存放文件
首先输入目录名称,minio系统称为水桶名称,创建成功后可以发现在本地数据文件目录下多了新建的文件夹。

7、上传、下载、分享

  • 进入新建的Buckets,点击Upload,可以上传文件(Upload File)和上传目录(Upload Folder)

  • 上传成功后,页面将显示上传文件的基本信息,勾选对应的文件,在页面右边可以进行下载、分享、查看和删除

  • 点击share后,页面将会给你一个访问链接

http://169.254.205.236:9000/images/1.jpeg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=8RA23YUTTBA6I0W1FU17%2F20220726%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20220726T063539Z&X-Amz-Expires=604800&X-Amz-Security-Token=eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3NLZXkiOiI4UkEyM1lVVFRCQTZJMFcxRlUxNyIsImV4cCI6MTY1ODgxOTQ4NywicGFyZW50IjoiYWRtaW53cyJ9.U5sHwHOPT_QmO-qL-eBk8eJN2s15B9noJ3Pqb-YSgX3d5tqPhKAO8ruenIj-uVy_SdytTk3YB5e3zaMJK1DDww&X-Amz-SignedHeaders=host&versionId=null&X-Amz-Signature=ceda32bd33b653c76d3075674b7921c53d438620a5db87b5e7fbbde5a7f8a08f

此链接很长,只需要访问输入http://169.254.205.236:9000/images/1.jpeg即可访问,但是需要修改Buckets的访问权限,修改如下第八点。

  • 而在本地文件夹下,则查看不了上传的文件,只能通过minio进行查看,本次存储的文件格式如下图

8、修改默认分享时间
minio系统默认分享时间是七天,七天后将查看不了文件。在分享页面可以通过修改时间来改变分享的时长,如果想永久访问就需要修改Buckets,也就是本目录的权限。
在创建新的Buckets时,系统默认分配private权限,创建成功后可以修改为public权限。步骤如下。将权限由private修改为public有两个优点:一是文件可以被永久访问,二是分享的链接可以很短

9、修改系统默认密码
修改系统配置文件config.json总是报错,不知道为啥
于是添加一个启动.bat文件进行修改,.bat文件内容如下,注意用户名和密码长度需要满足一定要求

set MINIO_ACCESS_KEY=adminabc
set MINIO_SECRET_KEY=12345678
minio.exe server MinIOData

centos安装MinIO

1、下载MinIO

wget https://dl.min.io/server/minio/release/linux-amd64/minio

2、设置执行权限

chmod 777 minio

3、添加数据文件目录

mkdir data

4、开发防火墙端口

firewall-cmd --zone=public --add-port=9000/tcp --permanent
firewall-cmd --reload

systemctl status firewalld  # 查看防火墙状态
systemctl stop firewalld    # 关闭防火墙

5、启动minIO

./minio server ./data


注意:这里出现了一个警告,意思是我的linux内核版本低于4.0.0,会出现一些性能问题,提示minio运行在4.0.0以上版本会更好。

启动成功后,操作就和Windows的操作一样,浏览器输入地址进行登录。

6、设置启动sh文件

export MINIO_ACCESS_KEY=admin       # 登录用户名
export MINIO_SECRET_KEY=12345678    # 用户密码
# --address=":9001"             修改端口号
# --console-address ":9002"     设置控制台端口
# >> fileName   表示日志输出
# nohup ... &   表示后台运行,当前控制端页面关闭程序服务依然运行
nohup ./minio server /root/data --address=":9001" --console-address ":9002" >> info.log &

docker安装minIO

1、拉取镜像

docker pull minio/minio

2、运行minio镜像
添加一个启动镜像的sh文件,内容如下。/root/mminio/data:/data表示将目录文件与启动时数据文件进行映射,/root/mminio/config表示保存配置文件

docker stop minio
docker rm minio

docker run -it --name minio -p 9000:9000 -p 9001:9001 -d \
  -e "MINIO_ROOT_USER=adminabc" \
  -e "MINIO_ROOT_PASSWORD=12345678" \
  -v /root/mminio/data:/data \
  -v /root/mminio/config:/root/.minio \
  minio/minio server /data --address ":9000" --console-address ":9001"

运行出现一个错误:
这是因为关闭防火墙导致问题,重新启动docker服务即可systemctl restart docker.service

重新运行启动minio镜像命令,即可成功启动

springboot整合MinIO

1、pom.xml文件引入依赖

<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>8.3.9</version>
</dependency>

2、yml文件配置

minio:
  endpoint: http://loacalhost:9000
  accessKey: adminws
  secretKey: 12345678
  bucketName: images

3、添加minio配置类

import io.minio.MinioClient;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * minio对象存储配置
 */

@Data
@Configuration
public class MinIOconfig {
    @Value("${minio.endpoint}")
    private String endpoint;
    @Value("${minio.accessKey}")
    private String accessKey;
    @Value("${minio.secretKey")
    private String secretKey;

    /**
     * 注入minio客户端
     * @return
     */
    @Bean
    public MinioClient minioClient(){
        return MinioClient.builder()
                .endpoint(endpoint)
                .credentials(accessKey,secretKey)
                .build();
    }

}

4、MinIO工具类

import io.minio.*;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Component
public class MinioUtils {
    @Autowired
    private MinioClient minioClient;

    @Value("${minio.bucketName}")
    private String bucketName;
    
    /**
     * 判断bucket是否存在
     * @param bucketName bucket名称
     * @return
     */
    public Boolean existBucket(String bucketName) {
        try {
            boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            return exists;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 创建bucket
     * @param bucketName 存储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
     * @param bucketName 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;
    }

    /**
     * 上传单个文件至 MinIo
     * MultipartFile
     * @param multipartFile 文件
     * @param catalogue     上传文件保存的目录名
     * @return 返回文件名
     */
    public String  uploadSingleFile(MultipartFile multipartFile, String catalogue){
        String fileExtension = multipartFile.getOriginalFilename().substring(multipartFile.getOriginalFilename().lastIndexOf(".")+1);
        List<String> arrayFileList = new ArrayList<>(Arrays.asList("jpeg","jpg","png","gif"));
        if(!arrayFileList.contains(fileExtension)){
            return "typeError";
        }
        long fileSize = multipartFile.getSize();
        if (fileSize > 1024*1024*2){
            return "sizeError";
        }
        String fileName = multipartFile.getOriginalFilename() + '_' + System.currentTimeMillis() + '.' + fileExtension;
        String fileName1 = '/' + bucketName + '/'  + fileName;
        InputStream inputStream = null;
        try{
            inputStream = multipartFile.getInputStream();
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(fileName1)
                    .stream(inputStream,inputStream.available(), -1)
                    .contentType(multipartFile.getContentType())
                    .build()
            );
        } catch (Exception e) {
            e.printStackTrace();
        } finally{
            if(inputStream != null){
                try{
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return fileName;
    }

    /**
     * 上传多个文件到 MinIo
     * 文件类型:MultipartFile
     * @param multipartFile     文件组
     * @param catalogue         上传文件保存的目录名
     * @return
     */
    public List<String> uploadMultiFile(MultipartFile[] multipartFile, String catalogue) {
        List<String> names = new ArrayList<>(multipartFile.length);
        for (MultipartFile file : multipartFile) {
            String fileName = uploadSingleFile(file, bucketName);
            names.add(fileName);
        }
        return names;
    }
    /**
     * 前端页面文件下载
     * @param minIoFilePath minIo中下载的文件路径
     * @param response      下载写入HttpServletResponse
     * @return
     * @throws Exception
     */
    public boolean downloadResponse(String minIoFilePath, HttpServletResponse response) throws Exception{
        try {
            GetObjectResponse objectResponse = minioClient.getObject(GetObjectArgs.builder()
                    .bucket(bucketName)
                    .object(minIoFilePath)
                    .build());
            response.setHeader("Content-Disposition","attachment;fileName=" + minIoFilePath.substring(minIoFilePath.lastIndexOf("/") + 1));
            response.setContentType("application/force-download");
            response.setCharacterEncoding("UTF-8");
            IOUtils.copy(objectResponse,response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }


    /**
     * 上传本地文件
     * @param path
     * @return
     */
    public String uploadLocalFile(String path) throws FileNotFoundException {
        File file = new File(path);
        String fileName = file.getName();
        String fileExtension = file.getName().substring(file.getName().lastIndexOf(".") + 1);
        List<String> arrayFileList = new ArrayList<>(Arrays.asList("jpeg","jpg","png","gif"));
        if(!arrayFileList.contains(fileExtension)){
            return "typeError";
        }
        if (file.length() > 1024*1024*2){
            return "sizeError";
        }
        FileInputStream fileInputStream = new FileInputStream(file);
        try {
            minioClient.putObject(PutObjectArgs.builder()
                    .stream(fileInputStream,file.length(),PutObjectArgs.MIN_MULTIPART_SIZE)
                    .bucket(bucketName)
                    .object(fileName)
                    .build());

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return fileName;
    }



    /**
     * 下载文件到本地
     * @param minIoFilePath minIo中下载的文件路径
     * @param localPath     下载到本地的文件路径
     * @return
     */
    public boolean downloadToLocal(String minIoFilePath, String localPath) throws Exception {
        try {
            minioClient.downloadObject(DownloadObjectArgs.builder()
                    .bucket(bucketName)
                    .object(minIoFilePath)
                    .filename(localPath)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 返回bucket中所有的文件名称,排除目录名
     * @param bucketName
     * @return
     */
    public List<String> allFileName(String bucketName){
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).build());
        List<String> filesName = new ArrayList<>();
        try{
            for (Result<Item> result : results){
                Item item = result.get();
                if (!item.isDir()){
                    filesName.add(item.objectName());
                }
            }
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
        return filesName;
    }

    /**
     * 删除bucket中的文件
     * @param fileName  要删除的文件名
     * @return
     */
    public Boolean removeFile(String fileName) throws Exception{
        try{
            minioClient.removeObject(RemoveObjectArgs.builder()
                    .bucket(bucketName)
                    .object(fileName)
                    .build());
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
        return true;
    }
}

5、MinIO控制类

@Autowired
private MinioUtils minioUtilS;
@Value("${minio.endpoint}")
private String endpoint;
@Value("${minio.bucketName}")
private String bucketName;
@Resource
private MinioClient minioClient;

@PostMapping("/upload")
public String upload(MultipartFile file) {

    String upload = minioUtilS.uploadSingleFile(file, "dir");

    return upload;
}

文章作者: zerollone
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 zerollone !
  目录