MinIO Java 对接试用:从连接、上传到下载的完整示例

MinIO Java 对接试用:从连接、上传到下载的完整示例

适用场景:Spring Boot 服务接入自建 MinIO,用于文件上传、下载、预览和删除。 本文示例不包含真实地址、账号或密钥,生产环境请使用环境变量或密钥管理服务。

一、先明确 MinIO 的对象模型

MinIO 兼容 S3,核心对象是:Endpoint(服务地址)、Bucket(桶)、Object(对象键)和凭证。对象键不是本地文件路径,例如 2026/08/report.pdf 只是一个字符串前缀。

二、准备试用环境

yaml 复制代码
MinIO Endpoint: http://127.0.0.1:9000
控制台地址:    http://127.0.0.1:9001
Bucket:        demo-files
Access Key:    通过环境变量注入
Secret Key:    通过环境变量注入

首次试用建议创建独立测试桶和最小权限账号,不要直接使用管理员账号连接业务系统。

三、引入 Java SDK

在 Maven 中加入依赖,版本由项目统一管理;不要在多个模块写不同版本。

xml 复制代码
<dependency>
  <groupId>io.minio</groupId>
  <artifactId>minio</artifactId>
  <version>${minio.version}</version>
</dependency>

四、Spring Boot 配置

ruby 复制代码
minio:
  endpoint: ${MINIO_ENDPOINT:http://127.0.0.1:9000}
  access-key: ${MINIO_ACCESS_KEY:}
  secret-key: ${MINIO_SECRET_KEY:}
  bucket: ${MINIO_BUCKET:demo-files}

不要把 secret-key 提交到 Git。开发机可以用 PowerShell 临时变量:

ini 复制代码
$env:MINIO_ENDPOINT="http://127.0.0.1:9000"
$env:MINIO_ACCESS_KEY="测试账号"
$env:MINIO_SECRET_KEY="测试密钥"
$env:MINIO_BUCKET="demo-files"

五、创建 MinioClient Bean

less 复制代码
@Configuration
public class MinioConfig {
    @Bean
    MinioClient minioClient(
            @Value("${minio.endpoint}") String endpoint,
            @Value("${minio.access-key}") String accessKey,
            @Value("${minio.secret-key}") String secretKey) {
        return MinioClient.builder()
                .endpoint(endpoint)
                .credentials(accessKey, secretKey)
                .build();
    }
}

六、封装常用操作

scss 复制代码
@Service
public class ObjectStorageService {
    private final MinioClient client;
    private final String bucket;
​
    public ObjectStorageService(MinioClient client,
                                @Value("${minio.bucket}") String bucket) {
        this.client = client;
        this.bucket = bucket;
    }
​
    public void ensureBucket() throws Exception {
        boolean exists = client.bucketExists(
                BucketExistsArgs.builder().bucket(bucket).build());
        if (!exists) {
            client.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
        }
    }
​
    public void upload(String objectName, InputStream input, long size,
                       String contentType) throws Exception {
        client.putObject(PutObjectArgs.builder()
                .bucket(bucket)
                .object(objectName)
                .stream(input, size, -1)
                .contentType(contentType)
                .build());
    }
​
    public InputStream download(String objectName) throws Exception {
        return client.getObject(GetObjectArgs.builder()
                .bucket(bucket).object(objectName).build());
    }
​
    public String presignedGetUrl(String objectName, int minutes) throws Exception {
        return client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                .method(Method.GET).bucket(bucket).object(objectName)
                .expiry(minutes, TimeUnit.MINUTES).build());
    }
​
    public void delete(String objectName) throws Exception {
        client.removeObject(RemoveObjectArgs.builder()
                .bucket(bucket).object(objectName).build());
    }
}

InputStream 用完必须关闭;大文件不要先读入 byte[]size 已知时传实际长度,未知时传 -1 并设置合理的分片大小。

七、提供一个联调接口

less 复制代码
@RestController
@RequestMapping("/api/files")
public class FileController {
    private final ObjectStorageService storage;
​
    @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public Map<String, String> upload(@RequestPart MultipartFile file) throws Exception {
        String objectName = UUID.randomUUID() + "-" +
                StringUtils.cleanPath(file.getOriginalFilename());
        storage.upload(objectName, file.getInputStream(), file.getSize(),
                Optional.ofNullable(file.getContentType())
                        .orElse(MediaType.APPLICATION_OCTET_STREAM_VALUE));
        return Map.of("objectName", objectName);
    }
}

测试:

bash 复制代码
curl -F "file=@./demo.txt" http://localhost:8080/api/files

八、试用验收清单

  1. 启动服务后能自动创建测试桶或检测到已有桶。
  2. 上传后在 MinIO Console 中能看到对象,大小和 Content-Type 正确。
  3. 下载内容与原文件校验和一致。
  4. 预签名 URL 在有效期内可访问,过期后失效。
  5. 删除对象后再次下载返回不存在。
  6. 错误日志不打印 Secret Key、完整预签名 URL 或用户隐私。

九、生产化注意事项

  • 桶默认保持私有,下载使用后端鉴权后生成短时预签名 URL。
  • 对象键使用业务 ID 加随机值,避免用户原文件名造成覆盖和路径穿越。
  • 上传前校验扩展名、MIME、大小和病毒扫描结果。
  • 大文件使用分片上传;失败时记录 uploadId 并提供清理策略。
  • 需要审计时保存对象键、业务单号、上传人、大小、哈希和时间,不保存密钥。
  • 跨服务器迁移前先确认版本控制、对象锁、生命周期和元数据是否需要保留。

本文示例适合首次验证 SDK 与 MinIO 连通性。真正上线前应补充统一异常、权限模型、断点续传、限流和监控。

相关推荐
程序员爱钓鱼1 小时前
Go 编程实战:函数 Function——参数、返回值与代码复用
后端·面试·go
To_OC7 小时前
跑了3个Docker容器后,我终于搞懂镜像和容器到底啥关系
后端·docker·容器
东风破_7 小时前
Docker 基础:为什么需要容器?
前端·后端·nginx
东风破_7 小时前
Docker 实战:使用 Nginx 作为容器入口代理 Node 服务
前端·后端·nginx
Lyyaoo.8 小时前
Spring Boot Validation 声明式参数校验
spring boot·后端·mysql
剩下了什么8 小时前
float32 与 float64 精度陷阱:如何在 Go 中避免错误使用
开发语言·后端·golang
Rain的Java大神之路8 小时前
介绍一下分布式事务
java·分布式·后端·spring·spring cloud·架构·springcloud
IT_陈寒12 小时前
Vue的响应式什么时候会失灵?这个坑我踩了
前端·人工智能·后端
Patrick_Wilson13 小时前
Web 认证方案技术指南
前端·后端·面试
与驴OO13 小时前
设计 Skill 系统,这 3 个坑我替你踩过了
后端·aigc