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工具类

oss对象存储-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
 * @see “http://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.文件操作类

oss对象存储-minio初体验
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.相关实体类

oss对象存储-minio初体验
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 oss对象存储-minio初体验
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

oss对象存储-minio初体验

oss对象存储-minio初体验

8.浏览器下载图片

url+bucketName+name:

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

 

参考文献:

https://www.cnblogs.com/lvlinguang/p/15774612.html
https://www.cnblogs.com/ssgeek/p/11072053.html
https://www.jianshu.com/p/f39e7255805b

 

上一篇:JDBC原生分页查询实现


下一篇:Day01MakeDown学习