Linux+Docker:3分钟实现MinIO在线部署与Java集成

Linux下使用Docker安装MinIO

1. 拉取 MinIO 镜像

docker pull minio/minio

2. 创建挂载目录

mkdir -p /opt/minio/data
mkdir -p /optl/minio/config

3. 检查端口占用

sudo lsof -i :9000
...

4. 启动 MinIO 容器

docker run --name minio \   # 容器名称
  -p 9010:9000 \            # 映射主机端口9010到容器端口9000
  -p 9020:9020 \            # 映射主机端口9020到容器端口9020
  -d --restart=always \     # 容器重启策略:系统启动时自动重启
  -e "MINIO_ROOT_USER=admin" \ # MinIO 管理员用户名
  -e "MINIO_ROOT_PASSWORD=admin2minio" \ # MinIO 管理员密码 这里的密码需要大于8位不如有报错
  -v "/opt/minio/data:/data" \ # 数据存储目录
  -v "/opt/minio/config:/root/.minio" \ # 配置文件目录
  minio/minio server /data --console-address ":9020" # 启动MinIO服务并指定控制台端口

5. 配置防火墙(如果有需要)

sudo firewall-cmd --zone=public --add-port=9010/tcp --permanent
sudo firewall-cmd --reload

6. 查看容器内的 MinIO 服务状态

docker exec -it minio /bin/sh
netstat -tuln

7. 检查容器日志

docker logs minio

可能会遇到的问题

1. RequestTimeTooSkewed 错误:

问题:客户端请求的时间与服务器时间的差异过大。MinIO要求客户端和服务器的时间同步。

解决方案:确保客户端和服务器的系统时间同步,或手动调整时间。查看时间的命令:

linux时间查看命令

date
2. MINIO_ROOT_PASSWORD 长度问题

问题: MinIO 对管理员密码有长度要求,密码必须至少 8 位字符,以保证安全性。短密码可能导致配置错误或安全问题。

解决方案:确保设置的 MINIO_ROOT_PASSWORD 至少为 8 位字符。检查密码是否符合 MinIO 的长度和复杂性要求。

  • 停止 MinIO 容器:
docker stop minio
  • 删除 MinIO 容器:
docker rm minio
  • 重新运行 MinIO 容器:
docker run --name minio \   
  -p 9010:9000 \            
  -p 9020:9020 \            
  -d --restart=always \     
  -e "MINIO_ROOT_USER=admin" \ 
  -e "MINIO_ROOT_PASSWORD=admin2minio" \ 
  -v "/opt/minio/data:/data" \ 
  -v "/opt/minio/config:/root/.minio" \ 
  minio/minio server /data --console-address ":9020"

确保重新运行时,密码满足要求,并且所有路径和端口正确配置。

Java项目集成MinIO

1. 添加Maven依赖

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

<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.9.3</version>
</dependency>

2. 配置 application.yml

minio:
  endpoint: http://ip:9010/  # MinIO服务的地址
  accessKey: 'your-access-key'  # MinIO的访问密钥
  secretKey: 'your-secret-key'  # MinIO的秘密密钥
  bucketName: test  # 默认的桶名称

3. 配置 MinIO 客户端

import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MinioConfig {

    @Value("${minio.endpoint}")
    private String endpoint;

    @Value("${minio.accessKey}")
    private String accessKey;

    @Value("${minio.secretKey}")
    private String secretKey;

    @Bean
    public MinioClient minioClient() {
        return MinioClient.builder()
                .endpoint(endpoint)
                .credentials(accessKey, secretKey)
                .build();
    }
}

4. MinIO 工具类

import com.github.pagehelper.util.StringUtil;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Component
public class MinIoUtils {

    @Autowired
    private MinioClient minioClient;

    @Value("${minio.bucketName}")
    private String bucketName;
    /**
     * description: 判断bucket是否存在,不存在则创建
     *
     * @return: void
     */
    public boolean existBucket(String name) {
        boolean exists;
        try {
            exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build());
            if (!exists) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());
                exists = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            exists = false;
        }
        return exists;
    }

    /**
     * 创建存储bucket
     *
     * @param bucketName 存储bucket名称
     * @return Boolean
     */
    public Boolean makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 删除存储bucket
     *
     * @param bucketName 存储bucket名称
     * @return Boolean
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * description: 上传文件
     *
     * @param file
     * @return: java.lang.String
     */
    public String upload(MultipartFile file, String fileName, String bucketNameStr) {
        InputStream in = null;
        try {
            if (StringUtil.isEmpty(bucketNameStr)) {
                bucketNameStr = bucketName;
            }
            in = file.getInputStream();
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketNameStr)
                    .object(fileName)
                    .stream(in, in.available(), -1)
                    .contentType(file.getContentType())
                    .build()
            );
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return bucketNameStr + "/" + fileName;
    }

    /**
     * description: 下载文件
     *
     * @param fileName
     * @return: org.springframework.http.ResponseEntity<byte [ ]>
     */
    public ResponseEntity<byte[]> download(String bucketNameStr, String fileName) {
        ResponseEntity<byte[]> responseEntity = null;
        InputStream in = null;
        ByteArrayOutputStream out = null;
        try {
            if (StringUtil.isEmpty(bucketNameStr)) {
                bucketNameStr = bucketName;
            }
            in = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
            out = new ByteArrayOutputStream();
            IOUtils.copy(in, out);
            //封装返回值
            byte[] bytes = out.toByteArray();
            HttpHeaders headers = new HttpHeaders();
            try {
                headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            headers.setContentLength(bytes.length);
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setAccessControlExposeHeaders(Arrays.asList("*"));
            responseEntity = new ResponseEntity<byte[]>(bytes, headers, HttpStatus.OK);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return responseEntity;
    }

    /**
     * 查看文件对象
     *
     * @param bucketName 存储bucket名称
     * @return 存储bucket内文件对象信息
     */
    public List<ObjectItem> listObjects(String bucketName) {
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).build());
        List<ObjectItem> objectItems = new ArrayList<>();
        try {
            for (Result<Item> result : results) {
                Item item = result.get();
                ObjectItem objectItem = new ObjectItem();
                objectItem.setObjectName(item.objectName());
                objectItem.setSize(item.size());
                objectItems.add(objectItem);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return objectItems;
    }

    /**
     * 批量删除文件对象
     *
     * @param bucketName 存储bucket名称
     * @param objects    对象名称集合
     */
    public Iterable<Result<DeleteError>> removeObjects(String bucketName, List<String> objects) {
        List<DeleteObject> dos = objects.stream().map(e -> new DeleteObject(e)).collect(Collectors.toList());
        Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build());
        return results;
    }

    /**
     * 根据文件名和桶获取文件路径
     *
     * @param bucketName 存储bucket名称
     */
    public String getFileUrl(String bucketName, String objectFile) {
        try {
            if(StringUtil.isEmpty(bucketName)){
                bucketName = this.bucketName;
            }
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(bucketName)
                    .object(objectFile)
                    .build()
            );
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

5. api

import com.alibaba.fastjson.JSONObject;
import com.njmh.authority.utils.MinIoUtils;
import com.njmh.authority.utils.ObjectItem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

@RestController
@RequestMapping("/minio")
public class MinioApi {


    @Autowired
    private MinIoUtils minioUtils;
    @Value("${minio.endpoint}")
    private String address;
    @Value("${minio.bucketName}")
    private String bucketName;

    @PostMapping("/upload")
    public Object upload(@RequestParam(value = "file") MultipartFile file, @RequestParam("bucketName") String bucketName) {
        return address + "/"  + minioUtils.upload(file, file.getOriginalFilename(), bucketName);
    }

    @PostMapping("/getListByBucket")
    public List<ObjectItem> getListByBucket() {
        List<ObjectItem> list = minioUtils.listObjects(bucketName);
        return list;
    }

    @PostMapping("/existBucket")
    public boolean existBucket(@RequestBody JSONObject jsonObject) {
        return minioUtils.existBucket(jsonObject.getString("name"));
    }

    @PostMapping("/makeBucket")
    public boolean makeBucket(@RequestBody JSONObject jsonObject) {
        return minioUtils.makeBucket(jsonObject.getString("name"));
    }

    @PostMapping("/removeBucket")
    public boolean removeBucket(@RequestBody JSONObject jsonObject) {
        return minioUtils.removeBucket(jsonObject.getString("name"));
    }

    @PostMapping("/getFileUrl")
    public String  getFileUrl(@RequestBody JSONObject jsonObject) {
        return minioUtils.getFileUrl(jsonObject.getString("bucketName"),jsonObject.getString("fileName"));
    }

    @GetMapping("/loadFile")
    @ResponseBody
    public ResponseEntity<?> loadFile(@RequestParam("bucketName") String bucketName,
                                      @RequestParam("filePath") String filePath) {
        return minioUtils.download(bucketName,filePath);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值