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
八、试用验收清单
- 启动服务后能自动创建测试桶或检测到已有桶。
- 上传后在 MinIO Console 中能看到对象,大小和 Content-Type 正确。
- 下载内容与原文件校验和一致。
- 预签名 URL 在有效期内可访问,过期后失效。
- 删除对象后再次下载返回不存在。
- 错误日志不打印 Secret Key、完整预签名 URL 或用户隐私。
九、生产化注意事项
- 桶默认保持私有,下载使用后端鉴权后生成短时预签名 URL。
- 对象键使用业务 ID 加随机值,避免用户原文件名造成覆盖和路径穿越。
- 上传前校验扩展名、MIME、大小和病毒扫描结果。
- 大文件使用分片上传;失败时记录 uploadId 并提供清理策略。
- 需要审计时保存对象键、业务单号、上传人、大小、哈希和时间,不保存密钥。
- 跨服务器迁移前先确认版本控制、对象锁、生命周期和元数据是否需要保留。
本文示例适合首次验证 SDK 与 MinIO 连通性。真正上线前应补充统一异常、权限模型、断点续传、限流和监控。