minio-docker

docker-compose

version: "3"
services:
  minio:
    image: minio/minio:latest
    container_name: minio
    restart: always
    ports:
      - "9000:9000"                                    
      - "9090:9090"                                    
    volumes:                                             
      - "./data:/data"
      - "./config:/root/.minio"
      - /etc/localtime:/etc/localtime:ro
    command: server /data --console-address ":9090" -address ":9000"   
    environment:
      MINIO_ROOT_USER: admin
      MINIO_ROOT_PASSWORD: admin123

还可选择其他镜像

minio/minio              RELEASE.2023-10-14T05-17-22Z.fips
minio/minio              RELEASE.2022-11-29T23-40-49Z 
minio/minio              RELEASE.2021-06-17T00-10-46Z 

使用方法都一致

可出现的问题

将桶设置为public

就是在web页面上无法设置 bucket 为 public

这个经过本人大量验证,就是网络问题,被服务器端拦截或断开;

解决方案一:登录的minio所安装的服务器上执行以下命令

curl -X PUT 
http://127.0.0.1:9090/api/v1/buckets/XXXXXX/set-policy      ## ip端口还有中间路径里面的XXXXX是桶名,记得修改
-H "Content-Type:application/json" 
-H "Cookie:token=AL0GUGqYRhLcuy82AN7bmc39nYkhhLY0aH2OIk+wEOywuS3TM2u5BaXAWaTYP7wVEUEuu3C23YcvGkoyFyqEwAsYXtQjrWL+Cal+tY5iJ0etGfz+9kCi+wivn9Lhp8WZZFuc7jrH7tziDRFJbrDcGeyNHWuoz16OKNUBD9mdPpdIDb7ARdjEds8rIhvWewUjRboBJSwBRWjsoVGmTWreNfuSscUaw7HGhgZQ29GBFX4+sVr7Nby3Rgkf0hBqLL97k3qhkSh6nL0f1znNO3DPVmefpVmKpGM69n7lft6r6su8DnPkMI2yc90/Nl85BpXF/DmFgM9Hr1KM6Vdtuc9CfWQdXhaaDXcBfh3TXvRUbel5T0lEmX4xMBDsjlZVExhQZUEZs3LpmmwiFtw+OsHdrC7htgOxoqeIzuwOZbY5tTNSpsioOM5UdItfnknWorbhgWKPtXcqmGbrSeVIxFEL6VIlx0Qy4Az/r+lUI5Fco1cUCSnzPXVtutEVl7+UnruXdr5OcEksfljJSfGu8SART5WlJ7Op5dcKxIHwmYQCzpY="              ## cookie的内容记得修改,浏览器上有
-d '{"access": "PUBLIC","definition": "{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n {\n   \"Effect\": \"Allow\",\n  \"Principal\": {\n   \"AWS\": [\n   \"*\"\n  ]\n  },\n  \"Action\": [\n  \"s3:GetBucketLocation\",\n \"s3:ListBucket\",\n  \"s3:ListBucketMultipartUploads\"\n            ],\n            \"Resource\": [\n                \"arn:aws:s3:::XXXXX\"\n            ]\n        },\n        {\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"*\"\n                ]\n            },\n            \"Action\": [\n                \"s3:AbortMultipartUpload\",\n                \"s3:DeleteObject\",\n                \"s3:GetObject\",\n                \"s3:ListMultipartUploadParts\",\n                \"s3:PutObject\"\n            ],\n            \"Resource\": [\n                \"arn:aws:s3:::XXXXX/*\"\n            ]\n        }\n    ]\n}"}'    ## 参数里面的 XXXXX 桶名记得修改

解决方案二:使用mc命令

如果不是用docker等其它容器安装的minio,则可在服务器上安装个mc管理工具,然后使用mc命令对minio进行开放public;

#  新版的minio无法通过管理界面操作,使用工具mc
参考文档: https://blog.csdn.net/weixin_42709585/article/details/148808966
               https://blog.csdn.net/m0_64787068/article/details/150557634

1、下载合适自己服务器的mc
https://dl.min.io/client/mc/release/linux-amd64/mc

2、上传到服务器授予权限、并放入当前目录/usr/local/bin/
chmod +x mc

sudo mv mc /usr/local/bin/mc

3、验证mc是否安装
mc alias set myminio http://127.0.0.1:9000 minioadmin minioadmin

mc admin info myminio
# 显示连接成功就对了

-----  从此开始可不用  begin-------
4、新增用户
mc admin user add myminio newuser newusersecret

    # 删除用户  mc admin user rm myminio newuser

5、添加分组  mc admin group add  myminio 组名 用户
mc admin group add  myminio testgroup newuser

6、查看分组
mc admin group ls  myminio

mc admin group info  myminio testgroup 

7、为刚刚创建的用户创建密钥,就是上传文件用的
mc admin accesskey create  myminio newuser --access-key newuseraccesskey   --secret-key newusersecretkey 

8、查看密钥   myminio:你得minio的实例名称
mc admin accesskey ls myminio

9、给用户添加读写权限
mc admin policy attach  myminio readwrite --user newuser

-----  从此开始可不用  end-------

创建
mc mb myminio/mybucket

mc anonymous set public myminio/mybucket

-- 设置桶不可遍历,当前桶为public,但是链接到桶的话(ip+端口+桶名),就会遍历出全部文件信息,需要禁止----开始----
-- 查看全部别名,实例
mc alias list
-- 查看某个 ALIAS 下的所有桶:
mc ls myminio
-- 创建minio.json文件(文件名叫啥都行)
-- 以下是文件内容(不包含本行,记得替换桶名)-- 开始----
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "*"
                ]
            },
            "Action": [
                "s3:GetBucketLocation",
                "s3:ListBucketMultipartUploads"
            ],
            "Resource": [
                "arn:aws:s3:::你的桶名"
            ]
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "*"
                ]
            },
            "Action": [
                "s3:AbortMultipartUpload",
                "s3:DeleteObject",
                "s3:GetObject",
                "s3:ListMultipartUploadParts",
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::你的桶名/*"
            ]
        }
    ]
}
-- 以上是文件内容(不包含本行)-- 结束----

-- 执行设置命令
mc anonymous set-json  上面那个的json文件.json  你的别名实例/你的桶名

-- 设置桶不可遍历,当前桶为public,但是链接到桶的话(ip+端口+桶名),就会遍历出全部文件信息,需要禁止----结束----


解决方案三:直接在页面上将minio设置为custom

# 记得替换桶名
{"access": "CUSTOM","definition": "{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n     
   {\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"AWS\": [\n      
   \"*\"\n                ]\n            },\n            \"Action\": [\n                \"s3:GetBucketLocation\",\n    
   \"s3:ListBucketMultipartUploads\"\n            ],\n            \"Resource\": [\n                \"arn:aws:s3:::你的桶名\"\n    
   ]\n        },\n        {\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"AWS\": [\n   
   \"*\"\n                ]\n            },\n            \"Action\": [\n                \"s3:AbortMultipartUpload\",\n        
   \"s3:DeleteObject\",\n                \"s3:GetObject\",\n                \"s3:ListMultipartUploadParts\",\n             
   \"s3:PutObject\"\n            ],\n            \"Resource\": [\n                \"arn:aws:s3:::你的桶名/*\"\n            ]\n    
   }\n    ]\n}"}

nginx配置


# minio文件服务,具体路径自己配置
    location /minio/ {
        proxy_pass   http://XXXX:9000/;
	proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Host $http_host;
    proxy_set_header X-NginX-Proxy true;

    proxy_connect_timeout 300;

    proxy_http_version 1.1;
    proxy_set_header Connection "";
    chunked_transfer_encoding off;
    }

Java代码

<dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>8.1.0</version>
        </dependency>
minio:
  # MinIO服务器地址
  endpoint: http://XXXX:9000
  # MinIO服务器访问凭据
  accessKey: admin
  secretKey: admin123
  # MinIO桶名称
  bucket: 自己设置的桶名
  # MinIO读取路径前缀,内网和外网
  readPathNw: http://XXXXXX:9000
  readPathWw: https://XXXX


import io.minio.MinioClient;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;  
  
  
@Data  
@Configuration  
@EnableConfigurationProperties({MinIOConfigProperties.class})  
//当引入FileStorageService接口时
@ConditionalOnClass(IMinioService.class)
public class MinIOConfig {
  
    @Autowired  
    private MinIOConfigProperties minIOConfigProperties;  
  
    @Bean  
    public MinioClient buildMinioClient() {  
        return MinioClient  
                .builder()  
                .credentials(minIOConfigProperties.getAccessKey(), minIOConfigProperties.getSecretKey())  
                .endpoint(minIOConfigProperties.getEndpoint())  
                .build();  
    }  
}

  
  
import lombok.Data;  
import org.springframework.boot.context.properties.ConfigurationProperties;  
  
import java.io.Serializable;  
  
@Data  
@ConfigurationProperties(prefix = "minio")  // 文件上传 配置前缀file.oss  
public class MinIOConfigProperties implements Serializable {  
  
    private String accessKey;  
    private String secretKey;  
    private String bucket;  
    private String endpoint;  
    private String readPathNw;
    private String readPathWw;
}



import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @title MinioServiceImpl
 * @description minio服务
 * @since 2024/9/3 16:54
 */
@Slf4j
@EnableConfigurationProperties(MinIOConfigProperties.class)
@Import(MinIOConfig.class)
@Component
public class MinioServiceImpl implements IMinioService {

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private MinIOConfigProperties minIOConfigProperties;

    private final static String separator = "/";

    /**
     * @param dirPath
     * @param filename  yyyy/mm/dd/file.jpg
     * @return
     */
    public String builderFilePath(String dirPath,String filename) {
        StringBuilder stringBuilder = new StringBuilder(50);
        if(!StringUtils.isEmpty(dirPath)){
            stringBuilder.append(dirPath).append(separator);
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        String todayStr = sdf.format(new Date());
        stringBuilder.append(todayStr).append(separator);
        stringBuilder.append(filename);
        return stringBuilder.toString();
    }

    /**
     *  上传文件
     * @param prefix  文件前缀
     * @param filename  文件名
     * @param inputStream 文件流
     * @return  文件全路径
     */
    @Override
    public Map<String, String> uploadFile(String prefix, String filename, InputStream inputStream) {
        Map<String, String> map = new HashMap<>(2);
        String filePath = builderFilePath(prefix, filename);
        try {
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .object(filePath)
//                    .contentType("image/jpg")
                    .bucket(minIOConfigProperties.getBucket()).stream(inputStream,inputStream.available(),-1)
                    .build();
            minioClient.putObject(putObjectArgs);
            // 内网地址
            StringBuilder nwUrlPath = new StringBuilder(minIOConfigProperties.getReadPathNw());
            nwUrlPath.append(separator+minIOConfigProperties.getBucket()).append(separator).append(filePath);
            // 外网地址
            StringBuilder wwUrlPath = new StringBuilder(minIOConfigProperties.getReadPathWw());
            wwUrlPath.append(separator+minIOConfigProperties.getBucket()).append(separator).append(filePath);
            map.put("nw", nwUrlPath.toString());
            map.put("ww", wwUrlPath.toString());
            return map;
        }catch (Exception ex){
            log.error("minio put file error.",ex);
            throw new RuntimeException("上传文件失败");
        }
    }

    /**
     *  上传html文件
     * @param prefix  文件前缀
     * @param filename   文件名
     * @param inputStream  文件流
     * @return  文件全路径
     */
    @Override
    public String uploadHtmlFile(String prefix, String filename,InputStream inputStream) {
        String filePath = builderFilePath(prefix, filename);
        try {
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .object(filePath)
                    .contentType("text/html")
                    .bucket(minIOConfigProperties.getBucket()).stream(inputStream,inputStream.available(),-1)
                    .build();
            minioClient.putObject(putObjectArgs);
            StringBuilder urlPath = new StringBuilder(minIOConfigProperties.getReadPathNw());
            urlPath.append(separator+minIOConfigProperties.getBucket());
            urlPath.append(separator);
            urlPath.append(filePath);
            return urlPath.toString();
        }catch (Exception ex){
            log.error("minio put file error.",ex);
            ex.printStackTrace();
            throw new RuntimeException("上传文件失败");
        }
    }

    /**
     * 删除文件
     * @param pathUrl  文件全路径
     */
    @Override
    public void delete(String pathUrl) {
        String key = pathUrl.replace(minIOConfigProperties.getEndpoint()+"/","");
        int index = key.indexOf(separator);
        String bucket = key.substring(0,index);
        String filePath = key.substring(index+1);
        // 删除Objects
        RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder().bucket(bucket).object(filePath).build();
        try {
            minioClient.removeObject(removeObjectArgs);
        } catch (Exception e) {
            log.error("minio remove file error.  pathUrl:{}",pathUrl);
            e.printStackTrace();
        }
    }


    /**
     * 下载文件
     * @param pathUrl  文件全路径
     * @return  文件流
     *
     */
    @Override
    public byte[] downLoadFile(String pathUrl)  {
        String key = pathUrl.replace(minIOConfigProperties.getEndpoint()+"/","");
        int index = key.indexOf(separator);
        String bucket = key.substring(0,index);
        String filePath = key.substring(index+1);
        InputStream inputStream = null;
        try {
            inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(minIOConfigProperties.getBucket()).object(filePath).build());
        } catch (Exception e) {
            log.error("minio down file error.  pathUrl:{}",pathUrl);
            e.printStackTrace();
        }

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buff = new byte[100];
        int rc = 0;
        while (true) {
            try {
                if (!((rc = inputStream.read(buff, 0, 100)) > 0)) break;
            } catch (IOException e) {
                e.printStackTrace();
            }
            byteArrayOutputStream.write(buff, 0, rc);
        }
        return byteArrayOutputStream.toByteArray();
    }
}


import java.io.InputStream;
import java.util.Map;

/**
 * @title IMinioService
 * @description minio服务
 * @since 2024/9/3 16:53
 */
public interface IMinioService {


    /**
     *  上传图片文件
     * @param prefix  文件前缀
     * @param filename  文件名
     * @param inputStream 文件流
     * @return  文件全路径
     */
    Map<String, String> uploadFile(String prefix, String filename, InputStream inputStream);

    /**
     *  上传html文件
     * @param prefix  文件前缀
     * @param filename   文件名
     * @param inputStream  文件流
     * @return  文件全路径
     */
    String uploadHtmlFile(String prefix, String filename,InputStream inputStream);

    /**
     * 删除文件
     * @param pathUrl  文件全路径
     */
    void delete(String pathUrl);

    /**
     * 下载文件
     * @param pathUrl  文件全路径
     * @return
     *
     */
    byte[]  downLoadFile(String pathUrl);

}


import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.UUID;

/**
 * <p>
 * 文件管理类
 * </p>
 *
 */
@RequiredArgsConstructor
@RestController
@RequestMapping("/file")
public class FileManageController {

    @Autowired
    private IMinioService service;
    /**
     * 上传文件
     * @param file
     * @return
     */
    @PostMapping("/uploadOneFile")
    public Map<String, String> uploadFile(@RequestParam("file") MultipartFile multipartFile) {
        try(InputStream inputStream = multipartFile.getInputStream()) {  // 将MultipartFile转换为InputStream
            // 上传到MinIO服务器
            // 这里的文件名可以生成随机的名称,防止重复
            // 获取真实文件名
            String originalFilename = multipartFile.getOriginalFilename();
            // 使用UUID作为文件名,获取后缀
            StringBuffer name = new StringBuffer(UUID.randomUUID().toString().replaceAll("-", ""));
            if(StringUtils.isNotBlank(originalFilename) && originalFilename.contains(".")){
                name.append(".").append(originalFilename.substring(originalFilename.lastIndexOf(".") + 1));
            }
            return service.uploadFile("第一级的文件目录名", String.valueOf(name), inputStream);
        } catch (IOException e) {
            // 处理异常,可能是getInputStream()失败
            return null;
        }

    }

}

posted @ 2024-09-03 18:16  窃窃私语QAQ  阅读(38)  评论(0)    收藏  举报