zoukankan      html  css  js  c++  java
  • oss对象存储minio初体验

    官方文档地址

    https://docs.min.io/
    http://docs.minio.org.cn/docs/

    JavaSDK: https://github.com/minio/minio-java

    什么是 MinIO?

    Minio 是个基于 Golang 编写的开源对象存储套件,基于Apache License v2.0开源协议,虽然轻量,却拥有着不错的性能。它兼容亚马逊S3云存储服务接口。可以很简单的和其他应用结合使用,例如 NodeJS、Redis、MySQL等。

    MinIO 的应用场景

    MinIO 的应用场景除了可以作为私有云的对象存储服务来使用,也可以作为云对象存储的网关层,无缝对接 Amazon S3 或者 MicroSoft Azure 。

    minio特点

    • 高性能:
      作为一款高性能存储,在标准硬件条件下,其读写速率分别可以达到 55Gb/s35Gb/s。并而 MinIO 支持一个对象文件可以是任意大小,从几kb到最大5T不等。

    • 可扩展:
      不同MinIO集群可以组成联邦,并形成一个全局的命名空间,并且支持跨越多个数据中心。

    • 云原生:
      容器化、基于K8S的编排、多租户支持。

    • Amazon S3兼容:
      使用 Amazon S3 v2 / v4 API。可以使用Minio SDK,Minio Client,AWS SDK 和 AWS CLI 访问Minio服务器。

    • 可对接多种后端存储:
      除了Minio自己的文件系统,还支持 DAS、 JBODs、NAS、Google云存储和 Azure Blob存储。

    window安装minio服务(本地)

    1.下载地址
    https://dl.min.io/server/minio/release/windows-amd64/minio.exe

    2.进入下载目录,调用cmd窗口,并执行如下命令:
    .\minio.exe server F:\minio\photos

    3.访问:http://127.0.0.1:9000/,进入minio服务器登录页面

    默认账号密码均为:minioadmin

    minio之java应用

    1.pom引入jar包依赖

    <!-- minio 文件服务客户端 -->
            <dependency>
                <groupId>io.minio</groupId>
                <artifactId>minio</artifactId>
                <version>6.0.11</version>
            </dependency>
            <!--hutool-->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>5.1.1</version>
            </dependency>

    2.添加配置文件

    • url:minio服务器的接口地址,不是访问地址
    • accessKey/secretKey:登录minio系统,新建Service Accounts
    config:
      minio:
        url: http://192.168.1.114:9000
        accessKey: MMXHB6HAXWCCVG8ULAVK
        secretKey: 7J7v99N0LubbzshG2UmOrID9j2PNMG5mpCnBENv1

    3.注册MinioClient

    package cn.cjq.config;
    
    import io.minio.MinioClient;
    import lombok.Data;
    import lombok.SneakyThrows;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    //import org.springframework.cloud.context.config.annotation.RefreshScope;
    
    @Data
    @Configuration
    @ConfigurationProperties(prefix = "config.minio")
    public class MinioConfig {
        /**
         * minio 服务地址 http://ip:port
         */
        private String url;
    
        /**
         * 用户名
         */
        private String accessKey;
    
        /**
         * 密码
         */
        private String secretKey;
    
        @SneakyThrows
        @Bean
    //    @RefreshScope
        public MinioClient minioClient(){
            return new MinioClient(url, accessKey, secretKey);
        }
    }

    4.minio工具类

    package cn.cjq.util;
    
    import cn.cjq.entity.MinioItem;
    import io.minio.MinioClient;
    import io.minio.ObjectStat;
    import io.minio.Result;
    import io.minio.messages.Bucket;
    import io.minio.messages.Item;
    import lombok.SneakyThrows;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.io.InputStream;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Optional;
    
    /**
     * minio工具类
     *
     * @author lvlinguang
     * @date 2022-01-07 12:26
     * @seehttp://docs.minio.org.cn/docs/master/java-client-api-reference*/
    @Component
    public class MinioUtils {
    
        @Autowired
        private MinioClient client;
    
        /**
         * 创建bucket
         *
         * @param bucketName
         */
        @SneakyThrows
        public void createBucket(String bucketName) {
            if (!client.bucketExists(bucketName)) {
                client.makeBucket(bucketName);
            }
        }
    
        /**
         * 获取所有bucket
         *
         * @return
         */
        @SneakyThrows
        public List<Bucket> listAllBuckets() {
            return client.listBuckets();
        }
    
        /**
         * bucket详情
         *
         * @param bucketName 名称
         * @return
         */
        @SneakyThrows
        public Optional<Bucket> getBucket(String bucketName) {
            return client.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
        }
    
        /**
         * 删除bucket
         *
         * @param bucketName 名称
         */
        @SneakyThrows
        public void removeBucket(String bucketName) {
            client.removeBucket(bucketName);
        }
    
        /**
         * 上传文件
         *
         * @param bucketName bucket名称
         * @param objectName 文件名称
         * @param stream     文件流
         * @throws Exception
         */
        @SneakyThrows
        public void uploadFile(String bucketName, String objectName, InputStream stream) throws Exception {
            this.uploadFile(bucketName, objectName, stream, (long) stream.available(), "application/octet-stream");
        }
    
        /**
         * 上传文件
         *
         * @param bucketName  bucket名称
         * @param objectName  文件名称
         * @param stream      文件流
         * @param size        大小
         * @param contextType 类型
         * @throws Exception
         */
        @SneakyThrows
        public void uploadFile(String bucketName, String objectName, InputStream stream, long size, String contextType) throws Exception {
            //如果bucket不存在,则创建
            this.createBucket(bucketName);
            client.putObject(bucketName, objectName, stream, size, null, null, contextType);
        }
    
        /**
         * 获取文件
         *
         * @param bucketName bucket名称
         * @param objectName 文件名称
         * @return
         */
        @SneakyThrows
        public InputStream getFile(String bucketName, String objectName) {
            return client.getObject(bucketName, objectName);
        }
    
        /**
         * 根据文件前置查询文件
         *
         * @param bucketName bucket名称
         * @param prefix     前缀
         * @param recursive  是否递归查询
         * @return
         */
        @SneakyThrows
        public List<MinioItem> listAllFileByPrefix(String bucketName, String prefix, boolean recursive) {
            List<MinioItem> objectList = new ArrayList<>();
            Iterable<Result<Item>> objectsIterator = client
                    .listObjects(bucketName, prefix, recursive);
            while (objectsIterator.iterator().hasNext()) {
                objectList.add(new MinioItem(objectsIterator.iterator().next().get()));
            }
            return objectList;
        }
    
        /**
         * 删除文件
         *
         * @param bucketName bucket名称
         * @param objectName 文件名称
         */
        @SneakyThrows
        public void removeFile(String bucketName, String objectName) {
            client.removeObject(bucketName, objectName);
        }
    
        /**
         * 获取文件外链
         *
         * @param bucketName bucket名称
         * @param objectName 文件名称
         * @param expires    过期时间 <=7
         * @return
         */
        @SneakyThrows
        public String getFileURL(String bucketName, String objectName, Integer expires) {
            return client.presignedGetObject(bucketName, objectName, expires);
        }
    
        /**
         * 获取文件信息
         *
         * @param bucketName bucket名称
         * @param objectName 文件名称
         * @return
         */
        @SneakyThrows
        public ObjectStat getFileInfo(String bucketName, String objectName) {
            return client.statObject(bucketName, objectName);
        }
    }
    View Code

    5.文件操作类

    package cn.cjq.util;
    
    import cn.cjq.entity.MinioObject;
    import cn.hutool.core.io.FileUtil;
    import cn.hutool.core.io.IoUtil;
    import cn.hutool.core.util.StrUtil;
    import lombok.SneakyThrows;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpStatus;
    import org.springframework.stereotype.Component;
    import org.springframework.web.multipart.MultipartFile;
    
    import javax.servlet.http.HttpServletResponse;
    import java.io.InputStream;
    import java.time.LocalDate;
    import java.util.UUID;
    
    /**
     * 系统文件工具类
     *
     * @author lvlinguang
     * @date 2021-02-28 12:30
     */
    @Component
    public class SysFileUtils {
    
        /**
         * 文件服务器中的目录分隔符
         */
        public static final String SEPRETOR = "/";
    
        @Autowired
        private MinioUtils minioUtils;
    
        /**
         * 文件上传
         *
         * @param object     文件对你
         * @param bucketName bucket名称
         * @return
         */
        @SneakyThrows
        public MinioObject uploadFile(MultipartFile object, String bucketName) {
            return this.uploadFile(object.getInputStream(), bucketName, object.getOriginalFilename());
        }
    
        /**
         * 文件上传
         *
         * @param object     文件对你
         * @param bucketName bucket名称
         * @param fileName   文件名
         * @return
         */
        @SneakyThrows
        public MinioObject uploadFile(MultipartFile object, String bucketName, String fileName) {
            return this.uploadFile(object.getInputStream(), bucketName, fileName);
        }
    
        /**
         * 文件上传
         *
         * @param object         文件对你
         * @param bucketName     bucket名称
         * @param randomFileName 文件名是否随机(是:按年/月/日/随机值储存,否:按原文件名储存)
         * @return
         */
        @SneakyThrows
        public MinioObject uploadFile(MultipartFile object, String bucketName, Boolean randomFileName) {
            //文件名
            String fileName = object.getOriginalFilename();
            if (randomFileName) {
                //扩展名
                String extName = FileUtil.extName(object.getOriginalFilename());
                if (StrUtil.isNotBlank(extName)) {
                    extName = StrUtil.DOT + extName;
                }
                //新文件名
                fileName = randomFileName(extName);
            }
            return this.uploadFile(object.getInputStream(), bucketName, fileName);
        }
    
        /**
         * 文件上传
         *
         * @param object     文件对你
         * @param bucketName bucket名称
         * @param fileName   文件名
         * @return
         */
        @SneakyThrows
        public MinioObject uploadFile(InputStream object, String bucketName, String fileName) {
            try {
                minioUtils.uploadFile(bucketName, fileName, object);
                return new MinioObject(minioUtils.getFileInfo(bucketName, fileName));
            } catch (Exception e) {
                throw new Exception(e);
            } finally {
                if (object != null) {
                    object.close();
                }
            }
        }
    
        /**
         * 下载文件
         *
         * @param response response
         * @param url      文件地址(/bucketName/fileName)
         */
        public void downloadFile(HttpServletResponse response, String url) {
            final String bucketName = getBucketName(url);
            final String filePath = getFilePath(url);
            this.downloadFile(response, bucketName, filePath);
        }
    
        /**
         * 下载文件
         *
         * @param response response
         * @param bucket   bucket名称
         * @param fileName 文件名
         */
        public void downloadFile(HttpServletResponse response, String bucket, String fileName) {
            try (InputStream inputStream = minioUtils.getFile(bucket, fileName)) {
                if ("jpg".equals(FileUtil.extName(fileName))) {
                    response.setContentType("image/jpeg");
                } else if ("png".equals(FileUtil.extName(fileName))) {
                    response.setContentType("image/png");
                } else {
                    response.setContentType("application/octet-stream; charset=UTF-8");
                }
                IoUtil.copy(inputStream, response.getOutputStream());
            } catch (Exception e) {
                e.printStackTrace();
                response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            }
        }
    
        /**
         * 获取链接地址的 文件名
         *
         * @param bucketFileUrl
         * @return
         */
        public static String getFilePath(String bucketFileUrl) {
            if (bucketFileUrl == null) {
                return null;
            }
            //去掉第一个分割符
            if (bucketFileUrl.startsWith(SEPRETOR)) {
                bucketFileUrl = bucketFileUrl.substring(1);
            }
            return bucketFileUrl.substring(bucketFileUrl.indexOf(SEPRETOR) + 1);
        }
    
    
        /**
         * 获取链接地址的 bucketName
         *
         * @param bucketFileUrl 地址(/{bucketName}/{path}/{fileName})
         * @return
         */
        public static String getBucketName(String bucketFileUrl) {
            if (bucketFileUrl == null) {
                return null;
            }
            //去掉第一个分割符
            if (bucketFileUrl.startsWith(SEPRETOR)) {
                bucketFileUrl = bucketFileUrl.substring(1);
            }
            return bucketFileUrl.substring(0, bucketFileUrl.indexOf(SEPRETOR));
        }
    
        /**
         * 生成新的文件名
         *
         * @param extName 扩展名
         * @return
         */
        public static String randomFileName(String extName) {
            LocalDate now = LocalDate.now();
            return now.getYear() + SEPRETOR +
                    getFullNumber(now.getMonthValue()) + SEPRETOR +
                    getFullNumber(now.getDayOfMonth()) + SEPRETOR +
                    UUID.randomUUID().toString().replace("-", "") + extName;
        }
    
        /**
         * 得到数字全称,带0
         *
         * @param number
         * @return
         */
        public static String getFullNumber(Integer number) {
            if (number < 10) {
                return "0" + number;
            }
            return number.toString();
        }
    }
    View Code

    6.相关实体类

    package cn.cjq.entity;
    
    import io.minio.messages.Item;
    import io.minio.messages.Owner;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    
    import java.util.Date;
    
    /**
     * @author Kartist
     */
    @Data
    @AllArgsConstructor
    public class MinioItem {
    
        private String objectName;
        private Date lastModified;
        private String etag;
        private Long size;
        private String storageClass;
        private Owner owner;
        private String type;
    
        public MinioItem(Item item) {
            this.objectName = item.objectName();
            this.lastModified = item.lastModified();
            this.etag = item.etag();
            this.size = (long) item.size();
            this.storageClass = item.storageClass();
            this.owner = item.owner();
            this.type = item.isDir() ? "directory" : "file";
        }
    }
    View Code
    package cn.cjq.entity;
    
    import io.minio.ObjectStat;
    import lombok.Data;
    
    import java.util.Date;
    @Data
    public class MinioObject {
        private String bucketName;
        private String name;
        private Date createdTime;
        private Long length;
        private String etag;
        private String contentType;
    
        public MinioObject(ObjectStat os) {
            this.bucketName = os.bucketName();
            this.name = os.name();
            this.createdTime = os.createdTime();
            this.length = os.length();
            this.etag = os.etag();
            this.contentType = os.contentType();
        }
    }
    View Code

    7.contrller入口

    package cn.cjq.controller;
    
    
    import cn.cjq.entity.MinioObject;
    import cn.cjq.util.SysFileUtils;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    import org.springframework.web.multipart.MultipartFile;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    
    @RestController
    @RequestMapping("/hello")
    @Slf4j
    public class HelloWorldController {
    
        @Autowired
        private SysFileUtils sysFileUtils;
    
        /**
         * minio文件上传
         *
         * @param file
         * @param bucketName
         * @return
         */
        @PostMapping("/upload")
        public MinioObject upload(@RequestPart("file") MultipartFile file, @RequestParam("bucketName") String bucketName) {
            final MinioObject minioObject = sysFileUtils.uploadFile(file, bucketName, true);
            return minioObject;
        }
    
        /**
         * minio文件下载
         *
         * @return
         */
        @GetMapping("/info/**")
        public void getFile(HttpServletRequest request, HttpServletResponse response) {
            final String uri = request.getRequestURI();
            String fullName = uri.replace("/hello/info", "");
            sysFileUtils.downloadFile(response, fullName);
        }
    }

    8.post测试minio文件上传

    192.168.1.114:8080/hello/upload

    9.浏览器下载图片

    url+bucketName+name:

    http://192.168.1.114:8080/hello/info/cjq/2022/01/11/1106ab4078b2496699f93c0af971c737.png

    10.浏览器通过地址直接访问图片

    方式一:

    通过minio服务器管理后台界面,找到对应的桶下面的指定文件,拿到Shareable Link地址即可

    http://112.124.45.118:9000/qrcode/2022/1/24/140d0850f19c421a82bca702cf33468c.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=fhrs.file%2F20220124%2F%2Fs3%2Faws4_request&X-Amz-Date=20220124T054433Z&X-Amz-Expires=432000&X-Amz-SignedHeaders=host&X-Amz-Signature=50ec66e00abd871c203dd14027e80208352afead7035d8fc5df225b15a00211e

    方式二:

    java上传文件到minio服务器之后,根据返回值MinioObject中的属性,得到桶和文件名(相对路径)

    例:   /qrcode/2022/1/24/140d0850f19c421a82bca702cf33468c.jpg

    设置桶的策略(Edit policy)

    minIO没有配置bucket策略,默认情况下,minIO没有配置匿名读写的权限

    在bucket菜单栏中点击Edit policy,新增Read权限,(Read Only 或 Read and Write均可),即可通过链接的方式直接访问该文件

    前端拼接minio服务器地址 :http://112.124.45.118:9000/qrcode/2022/1/24/140d0850f19c421a82bca702cf33468c.jpg

    注:

    由于浏览器的限制,当上传文件时,设置header为application/octet-stream时,浏览器打开链接会默认进行下载而不是在浏览器中加载文件,所以如果想要文件时直接打开,上传时则不要设置application/octet-stream

    参考文献:

    https://www.cnblogs.com/lvlinguang/p/15774612.html
    https://www.cnblogs.com/ssgeek/p/11072053.html
    https://www.jianshu.com/p/f39e7255805b
    https://blog.csdn.net/iKaChu/article/details/105809957

    当能力支撑不了野心时,就该静下心来学习!
  • 相关阅读:
    腾讯云大数据套件Hermes-MR索引插件使用总结
    【文智背后的奥秘】系列篇——文本聚类系统
    【文智背后的奥秘】系列篇——关键词智能提取
    微信Tinker的一切都在这里,包括源码(一)
    腾讯云CMQ消息队列在Windows环境下的使用
    树莓派使用modbus与stm32通信
    Ubuntu manjaro 17.10 UTC
    如何彻底禁止360浏览器弹窗
    CentOS DesktopEntry
    centos7 安装qt
  • 原文地址:https://www.cnblogs.com/1234cjq/p/15787364.html
Copyright © 2011-2022 走看看