MinIO Docker安装使用和整合

Docker安装

	docker run \
   -p 9000:9000 \
   -p 9001:9001 \
   --name minio \
   -d --restart=always \
   -e "MINIO_ROOT_USER=minioadmin" \
   -e "MINIO_ROOT_PASSWORD=minioadmin" \
   -e "MINIO_BROWSER_DEFAULT_LOCALE=zh_CN" \    #  这个好像没啥用
   -v /opt/minio/data:/data \
   -v /opt/minio/config:/root/.minio \
   minio/minio  server \
  /data --console-address ":9001" -address ":9000"
	

SpringBoot整合

maven

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

Configuration

import io.minio.MinioClient;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
@ConfigurationProperties(prefix = "minio")
public class MinioConfig {
    private String url;
    private String accessKey;
    private String secretKey;
    private String bucket;
    @Bean
    public MinioClient minioClient() {
        return MinioClient.builder()
                .endpoint(url)
                .credentials(accessKey, secretKey)
                .build();
    }

}

YAML

spring:
    application:
        name: jelly-minio
    servlet:
        multipart:
            enabled: true
            max-file-size: 100MB
            max-request-size: 100MB
server:
    port: 8088
    servlet:
        context-path: /minio

minio:
    url: http://192.168.83.128:9000
    accessKey: minioadmin
    secretKey: minioadmin
    bucket: blog

Util工具类

import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
public class MinioUtil {
    @Autowired
    private MinioClient client;
    @Value("${minio.bucket}")
    private String bucketName;

    //region   Operate Bucket
    public boolean bucketExists(String bucketName) {
        try {
            BucketExistsArgs bucketArgs = BucketExistsArgs
                    .builder().bucket(bucketName).build();

            return client.bucketExists(bucketArgs);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 创建桶命名规则
     * https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html
     * @param bucketName
     */
    public void createBucket(String bucketName) {
        try {
            if (!bucketExists(bucketName)) {
                MakeBucketArgs args = MakeBucketArgs.builder()
                        .bucket(bucketName)
                        .build();
                client.makeBucket(args);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void deleteBucket(String bucketName) {
        try {
            if (bucketExists(bucketName)) {
                RemoveBucketArgs args = RemoveBucketArgs.builder().bucket(bucketName).build();
                client.removeBucket(args);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public Optional<Bucket> getBucket(String bucketName) {
        try {
            return client.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public List<Bucket> listBuckets() {
        try {
            return client.listBuckets();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public String getBucketPolicy(String bucketName) {
        try {
            return client.getBucketPolicy(
                    GetBucketPolicyArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void setBucketPolicy(String bucketName, String config) {
        try {
            SetBucketPolicyArgs args = SetBucketPolicyArgs.builder()
                    .bucket(bucketName)
                    .config(config)
                    .build();
            client.setBucketPolicy(args);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    //endregion


    //region  Operate Files

    /**
     * 是否存在文件
     *
     * @param bucketName
     * @param objectName
     * @return
     */
    public boolean isObjectExist(String bucketName, String objectName) {
        try {
            StatObjectArgs args = StatObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build();
            return client.statObject(args).size() > 0;
        } catch (Exception e) {
            return false;
        }
    }


    public ObjectWriteResponse createFolder(String bucketName, String objectName) {
        try {
            return client.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .stream(new ByteArrayInputStream(new byte[]{}), 0, -1)
                            .build());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据前缀获取文件列表
     *
     * @param bucketName
     * @param prefix
     * @param recursive
     * @return
     */
    public List<Item> listObjectsByPrefix(String bucketName, String prefix, boolean recursive) {
        List<Item> itemList = new ArrayList<>();
        try {
            ListObjectsArgs args = ListObjectsArgs.builder()
                    .bucket(bucketName)
                    .prefix(prefix)
                    .recursive(recursive)
                    .build();
            Iterable<Result<Item>> items = client.listObjects(args);
            if (items != null) {
                for (Result<Item> result : items) {
                    Item item = result.get();
                    itemList.add(item);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return itemList;
    }

    /**
     * 获取文件流
     *
     * @param bucketName
     * @param objectName
     * @return
     */
    public InputStream getObject(String bucketName, String objectName) {
        try {
            GetObjectArgs args = GetObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build();
            return client.getObject(args);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 断点下载文件
     *
     * @param bucketName
     * @param objectName
     * @param offset
     * @param length
     * @return
     */
    public InputStream getObject(String bucketName, String objectName, long offset, long length) {
        try {
            GetObjectArgs args = GetObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .offset(offset)
                    .length(length)
                    .build();
            return client.getObject(args);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public Iterable<Result<Item>> listObjects(String bucketName, String prefix, boolean recursive) {
        try {
            ListObjectsArgs args = ListObjectsArgs.builder()
                    .bucket(bucketName)
                    .prefix(prefix)
                    .recursive(recursive)
                    .build();
            return client.listObjects(args);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public ObjectWriteResponse uploadFile(String bucketName, MultipartFile file, String objectName, String contentType) throws IOException {
        InputStream inputStream = file.getInputStream();
        try {
            return client.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .contentType(contentType)
                            .stream(inputStream, inputStream.available(), -1)
                            .build());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 上传文件并生成url
     *
     * @param fileName
     * @param filePath
     * @return url
     */
    public String upload(String bucketName, String fileName, String filePath) {
        try {
            UploadObjectArgs args = UploadObjectArgs.builder()
                    .bucket(bucketName)
                    .object(fileName)
                    .filename(filePath)
                    .build();
            client.uploadObject(args);
            return client.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .method(Method.GET)
                            .bucket(bucketName)
                            .object(fileName)
                            .expiry(2, TimeUnit.HOURS)
                            .build());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 上传图片
     * @param bucketName
     * @param imageBase64
     * @param imageName
     * @return
     */
    public ObjectWriteResponse uploadImage(String bucketName, String imageBase64, String imageName) {
        Assert.notNull(imageBase64, "imageBase64 can not be null");
        byte[] decode = Base64.getDecoder().decode(imageBase64);
        InputStream in = new ByteArrayInputStream(decode);
        String newName = System.currentTimeMillis() + "_" + imageName + ".jpg";
        String year = String.valueOf(new Date().getYear());
        String month = String.valueOf(new Date().getMonth());
        return uploadFile(bucketName, year + "/" + month + "/" + newName, in);
    }

    /**
     * 上传文件
     * @param bucketName
     * @param objectName
     * @param fileName
     * @return
     */
    public ObjectWriteResponse uploadFile(String bucketName, String objectName, String fileName) {
        try {
            return client.uploadObject(
                    UploadObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .filename(fileName)
                            .build());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 上传文件
     * @param bucketName
     * @param objectName
     * @param inputStream
     * @return
     */

    public ObjectWriteResponse uploadFile(String bucketName, String objectName, InputStream inputStream) {
        try {
            return client.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .stream(inputStream, inputStream.available(), -1)
                            .build());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 删除文件
     *
     * @param fileName
     * @return
     */
    public boolean removeFile(String bucketName,String fileName) {
        try {
            RemoveObjectArgs args = RemoveObjectArgs.builder()
                    .bucket(bucketName)
                    .object(fileName)
                    .build();
            client.removeObject(args);
            return true;
        } catch (Exception e) {
            //throw new RuntimeException(e);
            return false;
        }
    }

    /**
     * 批量删除文件
     * @param bucketName
     * @param keys
     */
    public void removeFiles(String bucketName, List<String> keys) {
        List<DeleteObject> objects = new LinkedList<>();
        keys.forEach(s -> {
            objects.add(new DeleteObject(s));
            try {
                removeFile(bucketName, s);
            } catch (Exception e) {

            }
        });
    }

    /**
     * 获取文件状态信息
     * @param bucketName
     * @param objectName
     * @return
     */
    public String getFileStatusInfo(String bucketName, String objectName) {
        try {
            return client.statObject(
                    StatObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build()).toString();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * copy
     * @param bucketName
     * @param objectName
     * @param srcBucketName
     * @param srcObjectName
     * @return
     */
    public ObjectWriteResponse copyFile(String bucketName, String objectName, String srcBucketName, String srcObjectName) {
        try {
            return client.copyObject(
                    CopyObjectArgs.builder()
                            .source(CopySource.builder().bucket(bucketName).object(objectName).build())
                            .bucket(srcBucketName)
                            .object(srcObjectName)
                            .build());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @SneakyThrows(Exception.class)
    public String getPresignedObjectUrl(String bucketName, String objectName, Integer expires) {
        GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()
                .expiry(expires)
                .bucket(bucketName)
                .object(objectName).build();
        return client.getPresignedObjectUrl(args);
    }
    @SneakyThrows(Exception.class)
    public String getPresignedObjectUrl(String bucketName, String objectName) {
        GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .method(Method.GET).build();
        return client.getPresignedObjectUrl(args);
    }



    //endregion

}

Test

import io.minio.messages.Bucket;
import io.minio.messages.Item;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.ClassPathResource;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Optional;

@SpringBootTest
class JellyMinioApplicationTests {
    @Autowired
    private MinioUtil minioUtil;
    @Test
    void upload() {
        String filename ="2024/X_4.jpg";
        String path="C:\\Users\\xiaohao\\Pictures\\4K壁纸\\X_4.jpg";
        String url= minioUtil.upload("blog",filename, path);
        System.out.println(url);
    }
    @Test
    void createBucket() {
        minioUtil.createBucket("myblog");
    }
    @Test
    void bucketExists() {
        boolean exists = minioUtil.bucketExists("myblog");
        System.out.println(exists);
    }
    @Test
    void deleteBucket() {
        minioUtil.deleteBucket("myblog");
    }
    @Test
    void getBucketPolicy() {
        String bucketPolicy = minioUtil.getBucketPolicy("myblog");
        System.out.println(bucketPolicy);
    }
    @Test
    void setBucketPolicy() throws IOException {
        ClassPathResource classPathResource = new ClassPathResource("a3.json");
        InputStream inputStream = classPathResource.getInputStream();
        String config = new String(inputStream.readAllBytes());
        minioUtil.setBucketPolicy("myblog", config);
    }

    @Test
    void listBuckets() {
        List<Bucket> buckets = minioUtil.listBuckets();
        for (Bucket bucket : buckets) {
            System.out.println(bucket.name());
        }
    }
    @Test
    void getBuckets() {
        Optional<Bucket> bucket = minioUtil.getBucket("myblog");
        System.out.println(bucket.get().name());
    }

    @Test
    void isObjectExist() {
        boolean objectExist = minioUtil.isObjectExist("blog", "X_4.jpg");
        System.out.println(objectExist);
    }

    @Test
    void listObjectsByPrefix() {
        List<Item> items = minioUtil.listObjectsByPrefix("blog", "2", true);
        for (Item item : items) {
            System.out.println(item.objectName());
        }
    }
    @Test
    void getFileStatusInfo(){
        String fileStatusInfo = minioUtil.getFileStatusInfo("blog", "X_4.jpg");
        System.out.println(fileStatusInfo);
    }
    @Test
    void getPresignedObjectUrl(){
        String presignedObjectUrl = minioUtil.getPresignedObjectUrl("blog", "X_4.jpg");
        System.out.println(presignedObjectUrl);

    }
    @Test
    void createFile(){
        minioUtil.createFolder("blog", "/2024/1/1/");
        String filename ="/2024/1/1/X_7.png";
        String path="C:\\Users\\xiaohao\\Pictures\\4K壁纸\\X_7.png";
        String url= minioUtil.upload("blog",filename, path);
        System.out.println(url);
    }
}

MinIO config 配置桶政策

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "*"
        ]
      },
      "Action": [
        "s3:ListBucket",
        "s3:ListBucketMultipartUploads",
        "s3:GetBucketLocation"
      ],
      "Resource": [
        "arn:aws:s3:::myblog"
      ]
    },
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "*"
        ]
      },
      "Action": [
        "s3:AbortMultipartUpload",
        "s3:DeleteObject",
        "s3:GetObject",
        "s3:ListMultipartUploadParts",
        "s3:PutObject"
      ],
      "Resource": [
        "arn:aws:s3:::myblog/*"
      ]
    }
  ]
}