一佳技术

easy-oss 使用文档

easy-oss 提供后端中立的对象存储客户端门面——业务层通过 ObjectStorage 接口完成上传/下载/元数据/预签名 URL,底层由 SPI 屏蔽 MinIO 等 S3 兼容后端差异。MinIO SDK 为 optional scope,按需引入并配置 easy.oss.* 即自动装配。上传用「端点 + 文件名」由 ObjectKeyFactory 生成对象 key,返回的 objectName 供下载/删除/预签名复用。


1. 引入依赖

<!-- 核心依赖(必需) -->
<dependency>
    <groupId>io.matrix</groupId>
    <artifactId>easy-oss</artifactId>
    <version>${revision}</version>
</dependency>

<!-- MinIO 后端(optional,引入后才会装配 MinIO Connector) -->
<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
</dependency>

minio 依赖版本由 kernel-dependencies BOM(minio.version)统一管理,无需手动指定。后续扩展 OSS/COS 时,只需替换为对应 SDK 并新增 Connector。


2. 配置 MinIO 后端

easy:
  oss:
    enabled: true                    # 总开关,默认 false
    default-bucket: matrix           # 默认桶(小写、3 位以上,上传时自动创建)
    tenant: default                  # 对象 key 的租户前缀
    minio:
      endpoint: http://127.0.0.1:9000
      access-key: admin
      secret-key: password
      region: us-east-1
      secure: false                  # 是否 HTTPS(endpoint 未带协议时按此补全)
属性默认值说明
easy.oss.enabledfalse总开关,false 时不创建任何 Bean
easy.oss.default-bucketmatrix默认桶,未显式指定 bucket 的调用走它
easy.oss.tenantdefault对象 key 的租户前缀
easy.oss.minio.endpoint—MinIO 服务地址
easy.oss.minio.access-key / secret-key—访问凭据
easy.oss.minio.regionus-east-1区域
easy.oss.minio.securefalse是否 HTTPS(endpoint 无协议时补全)

同一应用只启用一个后端:OssAutoConfiguration 用 @ConditionalOnBean(BackendObjectStorage) + @ConditionalOnMissingBean(ObjectStorage) 互斥,业务方也可自定义 ObjectStorage Bean 覆盖默认门面。


3. 上传文件

注入 ObjectStorage,按业务端点 + 文件名上传:

@Autowired
private ObjectStorage objectStorage;

// 字符串
FileObject fo = objectStorage.putString("report", "summary.txt", "hello", "text/plain");

// 字节
FileObject fo2 = objectStorage.putBytes("avatar", "head.png", bytes, "image/png");

// 流
FileObject fo3 = objectStorage.putStream("firmware", "fw.bin", inputStream, "application/octet-stream", size);

// 文件
FileObject fo4 = objectStorage.putFile("report", "export.xlsx", new File("/tmp/export.xlsx"), 
                                        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");

返回值 FileObject 的关键字段:

字段说明
objectName对象 key(含路径),作为后续下载/删除/预签名入参
bucket / contentType所属桶与 MIME 类型
size / etag字节数与服务端 ETag

4. 下载 / 元数据 / 删除

均以 objectName 为入参,内部自动落到默认桶:

// 下载为字节
byte[] bytes = objectStorage.getBytes(fo.getObjectName());

// 下载为流(记得 close)
try (InputStream in = objectStorage.getStream(fo.getObjectName())) { ... }

// 元数据
FileObject meta = objectStorage.statObject(fo.getObjectName());

// 删除
boolean ok = objectStorage.deleteObject(fo.getObjectName());

5. 预签名 URL

用于浏览器直下 / 直传大文件,后端不经过 JVM:

import java.time.Duration;

// 下载 URL(浏览器直接打开即可下载)
String getUrl = objectStorage.presignedGetUrl(fo.getObjectName(), Duration.ofMinutes(30));

// 上传 URL(浏览器 PUT 直传)
String putUrl = objectStorage.presignedPutUrl(fo.getObjectName(), "image/png", Duration.ofMinutes(10));

6. 桶管理与对象列表

// 桶是否存在 / 创建桶
boolean exists = objectStorage.bucketExists("another-bucket");
objectStorage.makeBucket("another-bucket", "us-east-1");

// 前缀检索对象
List<String> names = objectStorage.listObjects("matrix", "report/2026/");

7. 对象 key 规范与自定义

默认 key 格式:{tenant}/{endpoint}/{yyyy/MM/dd}/{uuid}-{name}.{ext}

  • 多租户隔离、按天分区前缀检索、UUID 防重名
  • 文件名中的路径分隔符与前导点会被清洗,防止越权穿越(../、..)

自定义 key 生成:实现 ObjectKeyFactory 并注册为 Bean(默认实现会被覆盖):

@Component
public class BizObjectKeyFactory implements ObjectKeyFactory {
    @Override
    public String newKey(String endpoint, String fileName) {
        return "biz/" + endpoint + "/" + System.currentTimeMillis() + "/" + fileName;
    }
}

ObjectKeyFactory、ObjectStorage 均可用 @ConditionalOnMissingBean 覆盖,业务方按需自定义。


8. 常见问题

问题说明
没有 ObjectStorage Bean检查 easy.oss.enabled=true、已引入 minio 依赖、已配置 easy.oss.minio.endpoint 三点
MinIO 桶不存在报错无需手动建桶,putObject 前会按 default-bucket 自动 bucketExists→makeBucket
报 NoSuchElementException / 连接失败校验 endpoint、access-key、secret-key,MinIO 服务是否可达、region 是否一致
预签名 URL 403确认对象存在、URL 未过期、凭据对该桶有权限
如何切换对象存储提供商当前仅 MinIO;新增 BackendObjectStorage 实现 + 对应 AutoConfiguration 即可,业务代码零改动
更多服务端能力(浏览器上传凭证、URL 代理)属中间件 easy-oss-server 规划范围,本期未实现