MinIO Linux 安装使用 & SpringBoot整合MinIO

Minio 是一个基于Apache License v2.0开源协议的对象存储服务。它可以运行在多种操作系统上,包括 Linux 和 Windows 等。
它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5TB不等。

Minio官网:https://min.io/
中文地址:https://www.minio.org.cn
中文文档:https://www.minio.org.cn/docs/minio/linux/index.html
演示:https://play.minio.org.cn 用户名:minioadmin、密码minioadmin

MinIO Linux 安装

单节点部署

Download: https://www.minio.org.cn/download.shtml#/linux
使用以下命令下载安装最新版本的稳定 MinIO二进制包, 并设置 $PATH :

[root@localhost ~]# wget https://dl.minio.org.cn/server/minio/release/linux-amd64/minio [root@localhost ~]# chmod +x minio [root@localhost ~]# sudo mv minio /usr/local/bin/ 

MinIO Linux 安装使用 & SpringBoot整合MinIO

创建 systemd 系统启动服务文件

创建 minio.service 启动文件,确保文件在 /usr/lib/systemd/system/minio.service

[root@localhost ~]# vi /usr/lib/systemd/system/minio.service 

minio.service

[Unit] Description=MinIO Documentation=https://min.io/docs/minio/linux/index.html Wants=network-online.target After=network-online.target AssertFileIsExecutable=/usr/local/bin/minio  [Service] WorkingDirectory=/usr/local  User=minio-user Group=minio-user ProtectProc=invisible  EnvironmentFile=-/etc/default/minio ExecStartPre=/bin/bash -c "if [ -z "${MINIO_VOLUMES}" ]; then echo "Variable MINIO_VOLUMES not set in /etc/default/minio"; exit 1; fi" ExecStart=/usr/local/bin/minio server $MINIO_OPTS $MINIO_VOLUMES  # MinIO RELEASE.2023-05-04T21-44-30Z adds support for Type=notify (https://www.freedesktop.org/software/systemd/man/systemd.service.html#Type=) # This may improve systemctl setups where other services use `After=minio.server` # Uncomment the line to enable the functionality # Type=notify  # Let systemd restart this service always Restart=always  # Specifies the maximum file descriptor number that can be opened by this process LimitNOFILE=65536  # Specifies the maximum number of threads this process can create TasksMax=infinity  # Disable timeout logic and wait until process is stopped TimeoutStopSec=infinity SendSIGKILL=no  [Install] WantedBy=multi-user.target  # Built for ${project.name}-${project.version} (${project.name}) 

默认情况下, minio.service 文件以 minio-user 用户和组的身份运行。 您可以使用 groupadduseradd 创建用户和组。 命令。下面的示例创建了用户和组,并设置了权限 来访问供 MinIO 使用的文件夹路径。这些命令通常 需要 root ( sudo ) 权限。

[root@localhost ~]# groupadd -r minio-user [root@localhost ~]# useradd -M -r -g minio-user minio-user # 存储路径 [root@localhost ~]# chown minio-user:minio-user /mnt/data chown: 无法访问"/mnt/data": 没有那个文件或目录 [root@localhost ~]# mkdir /mnt/data [root@localhost ~]# chown minio-user:minio-user /mnt/data 

本例中的驱动器路径由 MINIO_VOLUMES 环境变量指定。更改此处和环境变量文件中的值,使其与 MinIO 打算使用的驱动器路径相匹配。

创建环境变量文件

/etc/default/minio 创建环境变量文件。 MinIO 服务器容器可以将此文件作为所有 environment variables
下面的示例提供了一个起始环境文件:
密码不能小于8个字符, 否则无法启动

[root@localhost ~]# vi /etc/default/minio 
# MINIO_ROOT_USER and MINIO_ROOT_PASSWORD sets the root account for the MinIO server. # This user has unrestricted permissions to perform S3 and administrative API operations on any resource in the deployment. # Omit to use the default values 'minioadmin:minioadmin'. # MinIO recommends setting non-default values as a best practice, regardless of environment # 用户名长度不能小于3个字符 MINIO_ROOT_USER=admin # 密码不能小于8个字符, 否则无法启动 MINIO_ROOT_PASSWORD=minioadmin  # MINIO_VOLUMES sets the storage volume or path to use for the MinIO server.  MINIO_VOLUMES="/mnt/data"  # MINIO_OPTS sets any additional commandline options to pass to the MinIO server. # For example, `--console-address :9001` sets the MinIO Console listen port MINIO_OPTS="--console-address :9001" 

启动MinIO服务

启动 MinIO SNSD 部署即服务:

[root@localhost ~]# sudo systemctl start minio.service 

使用以下命令确认服务是否在线和功能正常:

[root@localhost ~]# sudo systemctl status minio.service [root@localhost ~]# journalctl -f -u minio.service 

自启动,将进程作为主机引导的一部分,在服务器重启的过程中该进程会自动重启,而不用再进行手动管理。

[root@localhost ~]# sudo systemctl enable minio.service 

MinIO Linux 安装使用 & SpringBoot整合MinIO

连接到MinIO服务

浏览器中输入:http://localhost:9001
登录MinIO的用户名和密码配置参数为 MINIO_ROOT_USERMINIO_ROOT_PASSWORD 这些配置可以在在容器指定的环境文件中进行修改。
MinIO Linux 安装使用 & SpringBoot整合MinIO
MinIO Linux 安装使用 & SpringBoot整合MinIO
您可以使用MinIO控制台进行一般管理任务,如身份和访问管理、指标和日志监控或服务器配置。每个MinIO服务器都包含其自己的嵌入式MinIO控制台。 如果您的本地主机防火墙允许外部访问控制台端口,则同一网络上的其他主机可以使用您的本地主机的IP地址或主机名访问控制台。

SpringBoot项目整合MinIO

https://mvnrepository.com/artifact/io.minio/minio

配置项

添加依赖

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

application.yml 配置 minio

 spring:      mvc:       hiddenmethod:         filter:           enabled: true   #设置文件上传大小限制   servlet:     multipart:       max-file-size: -1    #设置单个文件的大小 -1表示不限制       max-request-size: -1    #单次请求的文件的总大小 -1表示不限制  minio:   endpoint: http://172.16.3.195:9000   accessKey: admin   secretKey: minioadmin   bucketName: vipsoft-devminioadmin  

MinioConfig.java

package com.vipsoft.oss.config;   import io.minio.MinioClient; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;  @Configuration @ConfigurationProperties(prefix = "minio") public class MinioConfig {     /**      * 服务地址:http://172.16.3.195:9000      */     private String endpoint;      /**      * 用户名      */     private String accessKey;      /**      * 密码      */     private String secretKey;      /**      * 存储桶名称      */     private String bucketName;       @Bean     public MinioClient getMinioClient() {         MinioClient minioClient = MinioClient.builder().endpoint(endpoint)                 .credentials(accessKey, secretKey)                 .build();         return minioClient;     }      //todo 省去 getter & setter }  

工具类

MinioUtil.java

package com.vipsoft.oss.util;  import cn.hutool.core.io.IoUtil; import com.cuwor.oss.config.MinioConfig; import io.minio.*; import io.minio.MinioClient; import io.minio.http.Method; import io.minio.messages.Bucket; import io.minio.messages.DeleteError; import io.minio.messages.DeleteObject; import io.minio.messages.Item; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile;  import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.io.InputStream; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit;  @Component public class MinioUtil {      @Autowired     private MinioClient minioClient;      @Autowired     private MinioConfig minioConfig;      private static final int DEFAULT_EXPIRY_TIME = 7 * 24 * 3600;      /**      * 判断bucket是否存在      */     public boolean bucketExists(String bucketName) {         boolean exists;         try {             exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());         } catch (Exception e) {             e.printStackTrace();             exists = false;         }         return exists;     }       /**      * 创建存储桶,存在则不创建      */     public boolean makeBucket(String bucketName) {         boolean exists;         try {             exists = bucketExists(bucketName);             if (!exists) {                 minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());                 exists = true;             }         } catch (Exception e) {             e.printStackTrace();             exists = false;         }         return exists;     }      /**      * 删除存储桶 -- 有文件,不让删除      *      * @param bucketName 存储桶名称      * @return boolean      */     public boolean removeBucket(String bucketName) {         try {             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(RemoveBucketArgs.builder().bucket(bucketName).build());                 flag = bucketExists(bucketName);                 if (!flag) {                     return true;                 }             }             return false;         } catch (Exception e) {             e.printStackTrace();             return false;         }     }       /**      * 列出存储桶中的所有对象      *      * @param bucketName 存储桶名称      * @return Iterable<Result < Item>>      */     public Iterable<Result<Item>> listObjects(String bucketName) {         boolean flag = bucketExists(bucketName);         if (flag) {             return minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build());         }         return null;     }       /**      * 列出所有存储桶名称      *      * @return List<String>      */     public List<String> listBucketNames() {         List<String> bucketListName = new ArrayList<>();         try {             List<Bucket> bucketList = minioClient.listBuckets();             for (Bucket bucket : bucketList) {                 bucketListName.add(bucket.name());             }         } catch (Exception e) {             e.printStackTrace();         }         return bucketListName;     }      /**      * 列出存储桶中的所有对象名称      *      * @param bucketName 存储桶名称      * @return List<String>      */     public List<String> listObjectNames(String bucketName) {         List<String> listObjectNames = new ArrayList<>();         try {             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());                 }             }         } catch (Exception e) {             e.printStackTrace();         }         return listObjectNames;     }       /**      * 获取对象的元数据      *      * @param objectName 存储桶里的对象名称      * @return      */     public StatObjectResponse statObject(String objectName) {         StatObjectResponse statObject = null;         try {             statObject = minioClient.statObject(StatObjectArgs.builder().bucket(minioConfig.getBucketName()).object(objectName).build());         } catch (Exception e) {             e.printStackTrace();         }         return statObject;     }      //region 上传文件      /**      * 上传文件      *      * @param file       文件      * @param objectName 文件名称      */     public boolean uploadObject(MultipartFile file, String objectName) {         // 使用putObject上传一个文件到存储桶中。         try {             InputStream inputStream = file.getInputStream();             minioClient.putObject(PutObjectArgs.builder()                     .bucket(minioConfig.getBucketName())                     .object(objectName)                     .stream(inputStream, file.getSize(), -1)                     .contentType(file.getContentType())                     .build());             IoUtil.close(inputStream); //            return StrUtil.format("{}/{}/{}", minioConfig.getEndpoint(), minioConfig.getBucketName(), objectName);             return true;         } catch (Exception e) {             e.printStackTrace();         }         return false;     }      /**      * 通过InputStream上传对象      *      * @param objectName  存储桶里的对象名称      * @param inputStream 要上传的流      * @param contentType 上传的文件类型 例如 video/mp4  image/jpg      * @return boolean      */     public boolean uploadObject(InputStream inputStream, String objectName, String contentType) {         try {             minioClient.putObject(PutObjectArgs.builder().bucket(minioConfig.getBucketName()).object(objectName).stream(                             //不清楚文件的大小时,可以传-1,10485760。如果知道大小也可以传入size,partsize。                             inputStream, -1, 10485760)                     .contentType(contentType)                     .build());             IoUtil.close(inputStream);             StatObjectResponse statObject = statObject(objectName);             if (statObject != null && statObject.size() > 0) {                 return true;             }         } catch (Exception e) {             e.printStackTrace();         }         return false;     }      /**      * 通过文件上传到对象      *      * @param objectName 存储桶里的对象名称      * @param fileName   File name      * @return boolean      */     public boolean uploadObject(String objectName, String fileName) {         try {             minioClient.uploadObject(UploadObjectArgs.builder()                     .bucket(minioConfig.getBucketName())                     .object(objectName)                     .filename(fileName)                     .build());             StatObjectResponse statObject = statObject(objectName);             if (statObject != null && statObject.size() > 0) {                 return true;             }         } catch (Exception e) {             e.printStackTrace();         }          return false;     }      //endregion       /**      * 获取文件访问地址      *      * @param fileName 文件名称      */     public String getPresignedObjectUrl(String fileName) {         try {             return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()                     .method(Method.GET)                     .bucket(minioConfig.getBucketName())                     .object(fileName)                     .build()             );         } catch (Exception e) {             e.printStackTrace();         }         return null;     }      public String getObjectUrl(String objectName) {         String url = "";         try {             url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()                     .method(Method.GET)                     .bucket(minioConfig.getBucketName())                     .object(objectName)                     .build());          } catch (Exception e) {             e.printStackTrace();         }         return url;     }      /**      * 生成一个给HTTP GET请求用的presigned URL。      * 浏览器/移动端的客户端可以用这个URL进行下载,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。      *      * @param objectName 存储桶里的对象名称      * @param expires    失效时间(以小时单位),默认是7天,不得大于七天      * @return      */     public String getObjectUrl(String objectName, Integer expires) {         String url = "";         try {             if (expires < 1 || expires > DEFAULT_EXPIRY_TIME) {                 throw new Exception("Expires must be in range of 1 to " + DEFAULT_EXPIRY_TIME);             }             url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()                     .method(Method.GET)                     .bucket(minioConfig.getBucketName())                     .object(objectName)                     .expiry(expires, TimeUnit.HOURS)//动态参数                     // .expiry(24 * 60 * 60)//用秒来计算一天时间有效期                     // .expiry(1, TimeUnit.DAYS)//按天传参                     // .expiry(1, TimeUnit.HOURS)//按小时传参数                     .build());          } catch (Exception e) {             e.printStackTrace();         }         return url;     }      /**      * 下载文件,通过 HttpServletResponse 返回      *      * @param objectName 存储桶里的对象名称      */     public void downloadObject(HttpServletResponse response, String objectName) {         try {             InputStream file = minioClient.getObject(GetObjectArgs.builder()                     .bucket(minioConfig.getBucketName())                     .object(objectName)                     .build());             response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(objectName, "UTF-8"));             ServletOutputStream servletOutputStream = response.getOutputStream();             int len;             byte[] buffer = new byte[1024];             while ((len = file.read(buffer)) > 0) {                 servletOutputStream.write(buffer, 0, len);             }             servletOutputStream.flush();             file.close();             servletOutputStream.close();         } catch (Exception e) {             e.printStackTrace();         }     }       /**      * 下载并将文件保存到本地      *      * @param objectName 存储桶里的对象名称      * @param fileName   下载保存的文件名      * @return boolean      */     public boolean downloadObject(String objectName, String fileName) {         try {             StatObjectResponse statObject = statObject(objectName);             if (statObject != null && statObject.size() > 0) {                 minioClient.downloadObject(DownloadObjectArgs.builder()                         .bucket(minioConfig.getBucketName())                         .object(objectName)                         .filename(fileName)                         .build());                 return true;             }         } catch (Exception e) {             e.printStackTrace();         }         return false;     }       /**      * 以流的形式获取一个文件对象      *      * @param bucketName 存储桶名称      * @param objectName 存储桶里的对象名称      * @return InputStream      */     public InputStream getObject(String bucketName, String objectName) {         try {             StatObjectResponse statObject = statObject(objectName);             if (statObject != null && statObject.size() > 0) {                 InputStream stream = minioClient.getObject(GetObjectArgs.builder()                         .bucket(bucketName)                         .object(objectName)                         .build());                 return stream;             }         } catch (Exception e) {             e.printStackTrace();         }         return null;     }      /**      * 以流的形式获取一个文件对象(断点下载)      *      * @param bucketName 存储桶名称      * @param objectName 存储桶里的对象名称      * @param offset     起始字节的位置      * @param length     要读取的长度 (可选,如果无值则代表读到文件结尾)      * @return InputStream      */     public InputStream getObject(String bucketName, String objectName, long offset, Long length) {         try {             StatObjectResponse statObject = statObject(objectName);             if (statObject != null && statObject.size() > 0) {                 InputStream stream = minioClient.getObject(GetObjectArgs.builder()                         .bucket(bucketName)                         .object(objectName)                         .offset(1024L)                         .length(4096L)                         .build());                 return stream;             }         } catch (Exception e) {             e.printStackTrace();         }         return null;     }       /**      * 删除一个对象      *      * @param objectName 存储桶里的对象名称      */     public boolean removeObject(String objectName) {         try {             minioClient.removeObject(RemoveObjectArgs.builder().bucket(minioConfig.getBucketName()).object(objectName).build());             return true;         } catch (Exception e) {             e.printStackTrace();         }         return false;     }      /**      * 删除指定桶的多个文件对象,返回删除错误的对象列表,全部删除成功,返回空列表      *      * @param bucketName  存储桶名称      * @param objectNames 含有要删除的多个object名称的迭代器对象 eg:      *                    List<DeleteObject> objects = new LinkedList<>();      *                    objects.add(new DeleteObject("my-objectname1"));      *                    objects.add(new DeleteObject("my-objectname2"));      *                    objects.add(new DeleteObject("my-objectname3"));      * @return 如果有值,说明当前文件删除失败      */     public List<String> removeObjects(String bucketName, List<DeleteObject> objectNames) {         List<String> deleteErrorNames = new ArrayList<>();         try {             Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(objectNames).build());             for (Result<DeleteError> result : results) {                 DeleteError error = result.get();                 deleteErrorNames.add(error.objectName());             }         } catch (Exception e) {             e.printStackTrace();         }         return deleteErrorNames;     } }   

测试

package com.vipsoft.admin;  import cn.hutool.core.util.StrUtil; import com.cuwor.oss.util.MinioUtil; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.util.Assert;  @SpringBootTest public class MinioTest {      Logger logger = LoggerFactory.getLogger(this.getClass());      @Autowired     private MinioUtil minioUtil;       @Test     void makeBucketTest() {         boolean flag = minioUtil.makeBucket("public");         Assert.isTrue(flag, "查询异常");     }       @Test     void uploadObjectTest() {         boolean flag = minioUtil.uploadObject("/avatar/123.png", "D:\Users\Pictures\R-C.png");         Assert.isTrue(flag, "上传异常");     }       @Test     void getObjectUrlTest() {         String objectName="/avatar/123.png";         String url = minioUtil.getObjectUrl(objectName);         logger.info("url:{}", url);         url = minioUtil.getObjectUrl(objectName, 3);         logger.info("expires url:{}", url);         Assert.isTrue(StrUtil.isNotEmpty(url), "上传异常");     } } 

MinIO Linux 安装使用 &amp; SpringBoot整合MinIO

发表评论

相关文章