MinIo在Ubantu和Java中的整合

简介: 方法抛出的各种异常进行了捕获,然后打印了异常信息,目前这种处理逻辑,无论Minio是否发生异常,前端在上传文件时,总是会受到成功的响应信息。会处理所有Controller方法抛出的异常,因此Controller层就无序关注异常的处理逻辑了,因此Controller层代码可做出如下调整。MinIO是一个开源的对象存储服务器,兼容Amazon S3,性能卓越,适合存储非结构化数据,例如照片、视频、日志文件、备份和容器镜像等。功能,可以将所有处理异常的逻辑集中起来,进而统一处理所有异常,使代码更容易维护。

 1.MinIo在Ubantu中的部署

首先准备好一台已经安装好Ubantu系统的服务器

MinIO是一个开源的对象存储服务器,兼容Amazon S3,性能卓越,适合存储非结构化数据,例如照片、视频、日志文件、备份和容器镜像等。

1:更新系统

首先更新你的系统包:

sudo apt update 
sudo apt upgrade -y

image.gif

2:下载和安装MinIO

从MinIO官网下载最新的稳定版本二进制文件:

wget https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio
sudo mv minio /usr/local/bin/

image.gif

3:配置MinIO

  1. 创建MinIO用户: 出于安全考虑,建议以非root用户运行MinIO。
sudo useradd -r minio-user -s /sbin/nologin
  1. image.gif

2.创建目录: 创建MinIO数据和配置文件目录:

sudo mkdir /usr/local/share/minio
sudo mkdir /etc/minio
sudo chown -R minio-user:minio-user /usr/local/share/minio
sudo chown -R minio-user:minio-user /etc/minio

image.gif

3.设置环境变量: 创建一个文件来存储MinIO环境变量:

sudo nano /etc/default/minio

image.gif

将以下内容添加到文件中,用你自己的访问密钥和密钥替换YOUR_ACCESS_KEYYOUR_SECRET_KEY

MINIO_VOLUMES="/usr/local/share/minio/"
MINIO_OPTS="--address :9000 --console-address :9090"
MINIO_ACCESS_KEY="YOUR_ACCESS_KEY"
MINIO_SECRET_KEY="YOUR_SECRET_KEY"

image.gif

  • 9000: MinIO服务端口,即外部访问端口。
  • 9090: MinIO控制台端口,即内部访问端口。
  • YOUR_ACCESS_KEYYOUR_SECRET_KEY: 你的MinIO访问密钥和密钥。
  • /usr/local/share/minio/: MinIO数据目录。

4.创建systemd服务文件: 创建一个systemd服务文件来管理MinIO服务:

sudo nano /etc/systemd/system/minio.service

image.gif

添加以下内容:

[Unit]
Description=MinIO
Documentation=https://docs.min.io
Wants=network-online.target
After=network-online.target
[Service]
User=minio-user
Group=minio-user
EnvironmentFile=/etc/default/minio
ExecStart=/usr/local/bin/minio server $MINIO_OPTS $MINIO_VOLUMES
Restart=always
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target

image.gif

启动MinIO:

sudo systemctl daemon-reload
sudo systemctl start minio
sudo systemctl enable minio

image.gif

image.gif 编辑

可以选择用配置账号密码

访问MinIO管理页面

管理页面的访问地址为:http://yourIp:9090

API调用的 IP:http://yourIp:9000

注意

ip需要根据实际情况做出修改

mc配置Bucket权限:

步骤1:创建本地策略文件(自动处理格式,避免命令行解析错误)

echo '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetObject"],"Resource":["arn:aws:s3:::nursing-house/*"]}]}' > /tmp/public-policy.json

image.gif

步骤2:通过文件设置匿名访问权限

mc anonymous set-json /tmp/public-policy.json myminio/nursing-house

image.gif

步骤3:验证权限是否生效

mc anonymous get myminio/nursing-house

image.gif

步骤4:(可选)清理临时文件

rm -f /tmp/public-policy.json

image.gif

2.MinIO快速入门

2.1 MinIO核心概念

下面介绍MinIO中的几个核心概念,这些概念在所有的对象存储服务中也都是通用的。

  • 对象(Object)
    对象是实际的数据单元,例如我们上传的一个图片。
  • 存储桶(Bucket)
    存储桶是用于组织对象的命名空间,类似于文件夹。每个存储桶可以包含多个对象。
  • 端点(Endpoint)
    端点是MinIO服务器的网络地址,用于访问存储桶和对象,例如http://192.168.10.101:9000
    注意:
    9000为MinIO的API的默认端口,前边配置的9001以为管理页面端口。
  • Access Key 和 Secret Key
    Access Key是用于标识和验证访问者身份的唯一标识符,相当于用户名。
    Secret Key是与Access Key关联的密码,用于验证访问者的身份。

2.2 MinIO管理页面操作

  1. 登录
    管理页面的地址为http://192.168.10.101:9001,登录的用户名和密码为部署时在EnvironmentFile文件中配置的如下参数
MINIO_ROOT_USER=minioadmin
MINIO_ROOT_PASSWORD=minioadmin
  1. image.gif
  2. 创建存储桶 image.gif 编辑
  3. 上传图片
  • 找到目标桶 image.gif 编辑
  • 上传图片
    image.gif 编辑
  1. 访问图片
  • 图片URL
    由于MinIO提供了HTTP访问功能,所以可以通过浏览器直接访问对象。对象URL为MinIO的Endpoint+对象的存储路径,例如下图中的图片对象的URL为http:192.168.10.101:9000/test/公寓-外观.jpg
    image.gif 编辑
  • 访问权限不出意外的话,使用浏览器访问上述URL,会得到如下响应,很显然是没有访问权限。
<Error>
    <Code>AccessDenied</Code>
    <Message>Access Denied.</Message>
    <Key>A.jpg</Key>
    <BucketName>test</BucketName>
    <Resource>/test/A.jpg</Resource>
    <RequestId>177BC92022FC5684</RequestId>
  <HostId>dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8</HostId>
</Error>
  • image.gif 若想继续访问图片,需要修改图片所在桶的访问权限,如下图所示 image.gif 编辑如上图所示,可选的访问权限共有三个选项,分别是PrivatePublicCustom,具体说明如下
  • Private
    只允许桶的所有者对该桶进行读写。
  • Public
    允许所有人对该桶进行读写。
  • Custom
    自定义访问权限。
  • 若想将权限设置为只允许所有者写,但允许所有人读,就需要自定义访问权限。自定义访问权限,需要使用一个规定格式的JSON字符串进行描述,具体格式可参考官方文档
    例如以下JSON字符串表达的含义是:允许(Allow)所有人(*)读取(s3:GetObject)指定桶(test)的所有内容。
{
  "Statement" : [ {
    "Action" : "s3:GetObject",
    "Effect" : "Allow",
    "Principal" : "*",
    "Resource" : "arn:aws:s3:::test/*"
  } ],
  "Version" : "2012-10-17"
}

2.3 MinIO Java SDK

MinIO提供了多种语言的SDK供开发者使用,本项目需要用到Java SDK,下面通过一个简单案例熟悉一下其基本用法,具体内容可参考官方文档

  1. 创建一个Maven项目
  2. 引入如下依赖
<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>8.5.3</version>
</dependency>
  1. image.gif
  2. 编写如下内容
public class App {
    public static void main(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeyException {
        try {
            //构造MinIO Client
            MinioClient minioClient = MinioClient.builder()
                    .endpoint("http://192.168.10.101:9000")
                    .credentials("minioadmin", "minioadmin")
                    .build();
            //创建hello-minio桶
            boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket("hello-minio").build());
            if (!found) {
                //创建hello-minio桶
                minioClient.makeBucket(MakeBucketArgs.builder().bucket("hello-minio").build());
                //设置hello-minio桶的访问权限
                String policy = """
                        {
                          "Statement" : [ {
                            "Action" : "s3:GetObject",
                            "Effect" : "Allow",
                            "Principal" : "*",
                            "Resource" : "arn:aws:s3:::hello-minio/*"
                          } ],
                          "Version" : "2012-10-17"
                        }""";
                minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket("hello-minio").config(policy).build());
            } else {
                System.out.println("Bucket 'hello-minio' already exists.");
            }
            //上传图片
            minioClient.uploadObject(
                    UploadObjectArgs.builder()
                            .bucket("hello-minio")
                            .object("A.jpg")
                            .filename("D:\\workspace\\hello-minio\\src\\main\\resources\\A.jpg")
                            .build());
            System.out.println("上传成功");
        } catch (MinioException e) {
            System.out.println("Error occurred: " + e);
        }
    }
}
  1. image.gif
  2. 运行测试
    运行上述代码,然后查看MinIO管理页面,观察是否上传成功。

3.MioIo在实际项目中的整合应用

3.1图片上传流程

下图展示了新增房间或公寓时,上传图片的流程。

image.gif 编辑

可以看出图片上传接口接收的是图片文件,返回的Minio对象的URL。

3.2 图片上传接口开发

下面为该接口的具体实现

  • 配置Minio Client
  • 引入Minio Maven依赖
    common模块pom.xml文件增加如下内容:
<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
</dependency>
  • image.gif
  • 配置Minio相关参数
    application.yml中配置Minio的endpointaccessKeysecretKeybucketName等参数
minio:
  endpoint: http://<hostname>:<port>
  access-key: <access-key>
  secret-key: <secret-key>
  bucket-name: <bucket-name>
  • image.gif 注意:上述<hostname><port>等信息需根据实际情况进行修改。
  • common模块中创建com.atguigu.lease.common.minio.MinioProperties,内容如下
@ConfigurationProperties(prefix = "minio")
@Data
public class MinioProperties {
    private String endpoint;
    private String accessKey;
    private String secretKey;
    
    private String bucketName;
}
  • image.gif
  • common模块中创建com.atguigu.lease.common.minio.MinioConfiguration,内容如下
@Configuration
@EnableConfigurationProperties(MinioProperties.class)
public class MinioConfiguration {
    @Autowired
    private MinioProperties properties;
    @Bean
    public MinioClient minioClient() {
        return MinioClient.builder().endpoint(properties.getEndpoint()).credentials(properties.getAccessKey(), properties.getSecretKey()).build();
    }
}
  • image.gif
  • 开发图片上传接口
  • 编写Controller层逻辑
    FileUploadController中增加如下内容
@Tag(name = "文件管理")
@RequestMapping("/admin/file")
@RestController
public class FileUploadController {
    @Autowired
    private FileService service;
    @Operation(summary = "上传文件")
    @PostMapping("upload")
    public Result<String> upload(@RequestParam MultipartFile file) {
        String url = service.upload(file);
        return Result.ok(url);
    }
}
  • image.gif 说明:MultipartFile是Spring框架中用于处理文件上传的类,它包含了上传文件的信息(如文件名、文件内容等)。
  • 编写Service层逻辑
  • FileService中增加如下内容
String upload(MultipartFile file);
  • image.gif
  • FileServiceImpl中增加如下内容
@Autowired
private MinioProperties properties;
@Autowired
private MinioClient client;
@Override
public String upload(MultipartFile file) {
    try {
        boolean bucketExists = client.bucketExists(BucketExistsArgs.builder().bucket(properties.getBucketName()).build());
        if (!bucketExists) {
            client.makeBucket(MakeBucketArgs.builder().bucket(properties.getBucketName()).build());
            client.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(properties.getBucketName()).config(createBucketPolicyConfig(properties.getBucketName())).build());
        }
        String filename = new SimpleDateFormat("yyyyMMdd").format(new Date()) + "/" + UUID.randomUUID() + "-" + file.getOriginalFilename();
        client.putObject(PutObjectArgs.builder().
                bucket(properties.getBucketName()).
                object(filename).
                stream(file.getInputStream(), file.getSize(), -1).
                contentType(file.getContentType()).build());
        return String.join("/", properties.getEndpoint(), properties.getBucketName(), filename);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}
private String createBucketPolicyConfig(String bucketName) {
    return """
            {
              "Statement" : [ {
                "Action" : "s3:GetObject",
                "Effect" : "Allow",
                "Principal" : "*",
                "Resource" : "arn:aws:s3:::%s/*"
              } ],
              "Version" : "2012-10-17"
            }
            """.formatted(bucketName);
}
  • image.gif 注意:上述createBucketPolicyConfig方法的作用是生成用于描述指定bucket访问权限的JSON字符串。最终生成的字符串格式如下,其表示,允许(Allow)所有人(*)获取(s3:GetObject)指定桶(<bucket-name>)的内容。
{
  "Statement" : [ {
    "Action" : "s3:GetObject",
    "Effect" : "Allow",
    "Principal" : "*",
    "Resource" : "arn:aws:s3:::<bucket-name>/*"
  } ],
  "Version" : "2012-10-17"
}
  • image.gif
  • 问题说明
    上述代码只是对MinioClient方法抛出的各种异常进行了捕获,然后打印了异常信息,目前这种处理逻辑,无论Minio是否发生异常,前端在上传文件时,总是会受到成功的响应信息。可按照以下步骤进行操作,查看具体现象
    关闭虚拟机中的Minio服务
systemctl stop minio
  • image.gif 启动项目,并上传文件,观察接收的响应信息
  • 问题解决思路
    为保证前端能够接收到正常的错误提示信息,应该将Service方法的异常抛出到Controller方法中,然后在Controller方法中对异常进行捕获并处理。具体操作如下
    Service层代码
@Override
public String upload(MultipartFile file) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException{
    boolean bucketExists = minioClient.bucketExists(
            BucketExistsArgs.builder()
                    .bucket(properties.getBucketName())
                    .build());
    if (!bucketExists) {
        minioClient.makeBucket(
                MakeBucketArgs.builder()
                        .bucket(properties.getBucketName())
                        .build());
        minioClient.setBucketPolicy(
                SetBucketPolicyArgs.builder()
                        .bucket(properties.getBucketName())
                        .config(createBucketPolicyConfig(properties.getBucketName()))
                        .build());
    }
    String filename = new SimpleDateFormat("yyyyMMdd").format(new Date()) +
            "/" + UUID.randomUUID() + "-" + file.getOriginalFilename();
    minioClient.putObject(
            PutObjectArgs.builder()
                    .bucket(properties.getBucketName())
                    .stream(file.getInputStream(), file.getSize(), -1)
                    .object(filename)
                    .contentType(file.getContentType())
                    .build());
    return String.join("/",properties.getEndpoint(),properties.getBucketName(),filename);
}
  • image.gif
  • Controller层代码
public Result<String> upload(@RequestParam MultipartFile file) {
    try {
        String url = service.upload(file);
        return Result.ok(url);
    } catch (Exception e) {
        e.printStackTrace();
        return Result.fail();
    }
}
  • image.gif
  • 全局异常处理
    按照上述写法,所有的Controller层方法均需要增加try-catch逻辑,使用Spring MVC提供的全局异常处理功能,可以将所有处理异常的逻辑集中起来,进而统一处理所有异常,使代码更容易维护。
    具体用法如下,详细信息可参考官方文档
    common模块中创建com.atguigu.lease.common.exception.GlobalExceptionHandler类,内容如下
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result error(Exception e){
        e.printStackTrace();
        return Result.fail();
    }
}
  • image.gif 上述代码中的关键注解的作用如下
    @ControllerAdvice用于声明处理全局Controller方法异常的类
    @ExceptionHandler用于声明处理异常的方法,value属性用于声明该方法处理的异常类型
    @ResponseBody表示将方法的返回值作为HTTP的响应体
    注意:
    全局异常处理功能由SpringMVC提供,因此需要在common模块pom.xml中引入如下依赖
<!--spring-web-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  • image.gif
  • 修改Controller层代码
    由于前文的GlobalExceptionHandler会处理所有Controller方法抛出的异常,因此Controller层就无序关注异常的处理逻辑了,因此Controller层代码可做出如下调整。
public Result<String> upload(@RequestParam MultipartFile file) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
    String url = service.upload(file);
  return Result.ok(url);
}
  • image.gif
  • 由于公寓、房间的图片为公开信息,所以将其设置为所有人可访问。

3.3异常处理

  • 问题说明
    上述代码只是对MinioClient方法抛出的各种异常进行了捕获,然后打印了异常信息,目前这种处理逻辑,无论Minio是否发生异常,前端在上传文件时,总是会受到成功的响应信息。可按照以下步骤进行操作,查看具体现象
    关闭虚拟机中的Minio服务
systemctl stop minio
  • image.gif 启动项目,并上传文件,观察接收的响应信息
  • 问题解决思路
    为保证前端能够接收到正常的错误提示信息,应该将Service方法的异常抛出到Controller方法中,然后在Controller方法中对异常进行捕获并处理。具体操作如下
    Service层代码
@Override
public String upload(MultipartFile file) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException{
    boolean bucketExists = minioClient.bucketExists(
            BucketExistsArgs.builder()
                    .bucket(properties.getBucketName())
                    .build());
    if (!bucketExists) {
        minioClient.makeBucket(
                MakeBucketArgs.builder()
                        .bucket(properties.getBucketName())
                        .build());
        minioClient.setBucketPolicy(
                SetBucketPolicyArgs.builder()
                        .bucket(properties.getBucketName())
                        .config(createBucketPolicyConfig(properties.getBucketName()))
                        .build());
    }
    String filename = new SimpleDateFormat("yyyyMMdd").format(new Date()) +
            "/" + UUID.randomUUID() + "-" + file.getOriginalFilename();
    minioClient.putObject(
            PutObjectArgs.builder()
                    .bucket(properties.getBucketName())
                    .stream(file.getInputStream(), file.getSize(), -1)
                    .object(filename)
                    .contentType(file.getContentType())
                    .build());
    return String.join("/",properties.getEndpoint(),properties.getBucketName(),filename);
}
Controller层代码
public Result<String> upload(@RequestParam MultipartFile file) {
    try {
        String url = service.upload(file);
        return Result.ok(url);
    } catch (Exception e) {
        e.printStackTrace();
        return Result.fail();
    }
}
  • image.gif
  • 全局异常处理
    按照上述写法,所有的Controller层方法均需要增加try-catch逻辑,使用Spring MVC提供的全局异常处理功能,可以将所有处理异常的逻辑集中起来,进而统一处理所有异常,使代码更容易维护。
    具体用法如下,详细信息可参考官方文档
    common模块中创建com.atguigu.lease.common.exception.GlobalExceptionHandler类,内容如下
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result error(Exception e){
        e.printStackTrace();
        return Result.fail();
    }
}
  • image.gif 上述代码中的关键注解的作用如下
    @ControllerAdvice用于声明处理全局Controller方法异常的类
    @ExceptionHandler用于声明处理异常的方法,value属性用于声明该方法处理的异常类型
    @ResponseBody表示将方法的返回值作为HTTP的响应体
    注意:
    全局异常处理功能由SpringMVC提供,因此需要在common模块pom.xml中引入如下依赖
<!--spring-web-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  • image.gif
  • 修改Controller层代码
    由于前文的GlobalExceptionHandler会处理所有Controller方法抛出的异常,因此Controller层就无序关注异常的处理逻辑了,因此Controller层代码可做出如下调整。
public Result<String> upload(@RequestParam MultipartFile file) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
    String url = service.upload(file);
  return Result.ok(url);
}
  • image.gif


相关文章
|
5天前
|
人工智能 JSON 机器人
让龙虾成为你的“公众号分身” | 阿里云服务器玩Openclaw
本文带你零成本玩转OpenClaw:学生认证白嫖6个月阿里云服务器,手把手配置飞书机器人、接入免费/高性价比AI模型(NVIDIA/通义),并打造微信公众号“全自动分身”——实时抓热榜、AI选题拆解、一键发布草稿,5分钟完成热点→文章全流程!
10721 62
让龙虾成为你的“公众号分身” | 阿里云服务器玩Openclaw
|
4天前
|
人工智能 IDE API
2026年国内 Codex 安装教程和使用教程:GPT-5.4 完整指南
Codex已进化为AI编程智能体,不仅能补全代码,更能理解项目、自动重构、执行任务。本文详解国内安装、GPT-5.4接入、cc-switch中转配置及实战开发流程,助你从零掌握“描述需求→AI实现”的新一代工程范式。(239字)
3063 126
|
1天前
|
人工智能 自然语言处理 供应链
【最新】阿里云ClawHub Skill扫描:3万个AI Agent技能中的安全度量
阿里云扫描3万+AI Skill,发现AI检测引擎可识别80%+威胁,远高于传统引擎。
1195 1
|
11天前
|
人工智能 JavaScript API
解放双手!OpenClaw Agent Browser全攻略(阿里云+本地部署+免费API+网页自动化场景落地)
“让AI聊聊天、写代码不难,难的是让它自己打开网页、填表单、查数据”——2026年,无数OpenClaw用户被这个痛点困扰。参考文章直击核心:当AI只能“纸上谈兵”,无法实际操控浏览器,就永远成不了真正的“数字员工”。而Agent Browser技能的出现,彻底打破了这一壁垒——它给OpenClaw装上“上网的手和眼睛”,让AI能像真人一样打开网页、点击按钮、填写表单、提取数据,24小时不间断完成网页自动化任务。
2554 6
|
25天前
|
人工智能 JavaScript Ubuntu
5分钟上手龙虾AI!OpenClaw部署(阿里云+本地)+ 免费多模型配置保姆级教程(MiniMax、Claude、阿里云百炼)
OpenClaw(昵称“龙虾AI”)作为2026年热门的开源个人AI助手,由PSPDFKit创始人Peter Steinberger开发,核心优势在于“真正执行任务”——不仅能聊天互动,还能自动处理邮件、管理日程、订机票、写代码等,且所有数据本地处理,隐私完全可控。它支持接入MiniMax、Claude、GPT等多类大模型,兼容微信、Telegram、飞书等主流聊天工具,搭配100+可扩展技能,成为兼顾实用性与隐私性的AI工具首选。
24364 122

热门文章

最新文章