1. 程式人生 > 其它 >oss物件儲存-minio初體驗

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

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

當能力支撐不了野心時,就該靜下心來學習!