Skip to content

About

oss-spring-boot-starter is a toolkit based on Spring Boot and Amazon S3. With this toolkit, you can easily perform operations related to OSS (Object Storage Service) storage. 基于springboot的对象存储工具。

Topics

Resources

Stars

9 stars

Watchers

1 watching

Forks

Latest commit

 

History

235 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

oss-spring-boot-starter

Java CI Coverage Status GitHub Release Maven Central Version License

基于 AWS S3 SDK v2 的对象存储 Spring Boot Starter,支持 Spring Boot 2、Spring Boot 3、Spring Boot 4。项目按使用方式拆分为基础 Java API、内置 REST 接口、OpenAPI 注解元数据三类 Starter。

oss-spring-boot-starter 主图

示例工程入口:samples/README.md

前端在线演示页(GitHub Pages):https://weimin96.github.io/oss-spring-boot-starter/

该页面只托管前端静态资源

演示效果

分片上传

分片上传演示

全部功能

全部功能演示

能力概览

核心能力

  • 文件操作:OssTemplate 提供统一入口,支持上传、下载、复制、移动、删除
  • 大文件处理:分片上传、分片下载,完整支持 HTTP Range 断点续传
  • 流式解压:ZIP 文件流式解压,支持跨 Bucket 和按前缀过滤
  • 预签名 URL:生成下载/上传预签名,支持临时授权访问
  • 标签管理:对象标签和 Bucket 标签的 CRUD 操作
  • MinIO 事件监听:oss.type=minio 时支持对象创建、删除事件监听

查询能力

  • 多种列表模式:递归列表、层级列举、游标分页懒加载
  • 目录树:完整树形结构、关键字搜索、仅目录树
  • 元数据查询:对象信息、连接测试、Bucket 详情

Bucket 管理

  • ACL 权限控制、版本控制、时间回滚
  • 生命周期规则、CORS 配置、策略管理
  • 服务端加密、公共访问屏蔽

多端点支持

  • 基础 Java API:直接使用 OssTemplate
  • 内置 REST 接口:Web Starter 提供完整 HTTP 端点
  • OpenAPI 元数据:OpenAPI Starter 输出 Swagger 注解

兼容性与部署

  • 存储类型:MinIO、腾讯云 COS、华为云 OBS、通用 S3 兼容服务
  • Spring Boot 版本:2.x(javax.servlet)、3.x/4.x(jakarta.servlet)
  • 模块化设计:按需引入,避免不必要的依赖

选择依赖

使用场景 Spring Boot 2 Spring Boot 3 Spring Boot 4
只使用 Java API oss-spring-boot2-starter oss-spring-boot3-starter oss-spring-boot4-starter
使用 REST 接口(不带swagger) oss-spring-boot2-web-starter oss-spring-boot3-web-starter oss-spring-boot4-web-starter
使用 REST 接口(带swagger) oss-spring-boot2-openapi-starter oss-spring-boot3-openapi-starter oss-spring-boot4-openapi-starter

基础 Starter 只创建 OssTemplate,不注册 REST 控制器。需要 HTTP 接口时请选择 Web Starter 或 OpenAPI Starter。

OpenAPI Starter 只提供 Swagger 注解元数据,不内置 Swagger UI。宿主应用可以继续使用自己的 springdoc 或其他 OpenAPI 集成。

安装

Spring Boot 3 基础 Java API 示例:

<dependency>
    <groupId>io.github.weimin96</groupId>
    <artifactId>oss-spring-boot3-starter</artifactId>
    <version>3.3.3</version>
</dependency>

Spring Boot 3 内置 REST 接口示例:

<dependency>
    <groupId>io.github.weimin96</groupId>
    <artifactId>oss-spring-boot3-web-starter</artifactId>
    <version>3.3.3</version>
</dependency>

Spring Boot 3 OpenAPI 注解接口示例:

<dependency>
    <groupId>io.github.weimin96</groupId>
    <artifactId>oss-spring-boot3-openapi-starter</artifactId>
    <version>3.3.3</version>
</dependency>

Spring Boot 4 基础 Java API 示例:

<dependency>
    <groupId>io.github.weimin96</groupId>
    <artifactId>oss-spring-boot4-starter</artifactId>
    <version>3.3.3</version>
</dependency>

Spring Boot 4 内置 REST 接口示例:

<dependency>
    <groupId>io.github.weimin96</groupId>
    <artifactId>oss-spring-boot4-web-starter</artifactId>
    <version>3.3.3</version>
</dependency>

Spring Boot 4 OpenAPI 注解接口示例:

<dependency>
    <groupId>io.github.weimin96</groupId>
    <artifactId>oss-spring-boot4-openapi-starter</artifactId>
    <version>3.3.3</version>
</dependency>

Web Starter 和 OpenAPI Starter 的 Spring Web、Validation 依赖在本项目中按 provided 处理。宿主 Web 应用需要已经引入以下依赖;如果你的应用已经有它们,不需要重复声明。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

基础配置

oss:
  enable: true
  endpoint: http://127.0.0.1:9000
  bucket-name: oss-sample
  auto-create-bucket: true
  access-key: minioadmin
  secret-key: minioadmin
  type: minio
  max-connections: 50
  connection-timeout: 10000
  connection-acquisition-timeout: 10000
  api-call-timeout: 600000
  api-call-attempt-timeout: 120000
  multipart-threshold-in-mb: 10
  part-size-in-mb: 10
  http:
    enable: true
    prefix: /api
  event:
    enable: false
    bucket-name: oss-sample
    events:
      - s3:ObjectCreated:*
      - s3:ObjectRemoved:*
    prefix: ""
    suffix: ""
    reconnect-interval: 5s

spring:
  servlet:
    multipart:
      max-file-size: 500MB
      max-request-size: 500MB

只使用基础 Java API 时,可以不配置 oss.http,或保持 oss.http.enable=false。

配置项

配置项 类型 默认值 说明
oss.enable boolean false 是否启用自动配置
oss.endpoint String 无 对象存储服务端点,启用后必填
oss.bucket-name String 无 默认 Bucket 名称,多数默认 Bucket 操作需要配置
oss.auto-create-bucket boolean false 默认 Bucket 不存在时是否自动创建
oss.access-key String 无 访问密钥 ID,启用后必填
oss.secret-key String 无 访问密钥,启用后必填
oss.type String 无 存储类型,常用值为 minio、cos、obs
oss.max-connections int 50 最大连接数;分片服务端复制并发度取该值与 8 的较小值
oss.connection-timeout long 10000 连接超时时间,单位毫秒
oss.connection-acquisition-timeout long 10000 从连接池获取连接的最大等待时间,单位毫秒
oss.api-call-timeout long 600000 单次 API 调用总超时时间,单位毫秒
oss.api-call-attempt-timeout long 120000 单次 API 尝试超时时间,单位毫秒,不能超过调用总超时
oss.multipart-threshold-in-mb int 10 启用 multipart 的对象大小阈值,最小值为 5 MB
oss.part-size-in-mb int 10 multipart 分片大小基准,范围为 5–5120 MB,也用于分片服务端复制
oss.http.enable boolean false 是否注册内置 REST 接口
oss.http.prefix String 空字符串 REST 接口路径前缀
oss.event.enable boolean false 是否启用 MinIO 对象事件监听
oss.event.bucket-name String 默认 Bucket 监听事件的 Bucket 名称
oss.event.events List 创建、删除事件 监听的 S3 事件名称
oss.event.prefix String 空字符串 只监听指定对象前缀
oss.event.suffix String 空字符串 只监听指定对象后缀
oss.event.reconnect-interval Duration 5s 事件连接断开后的重连间隔

客户端行为

底层使用标准 Java S3AsyncClient、Netty 异步 HTTP client 和 S3TransferManager。oss.max-connections 控制 HTTP 最大并发连接数,oss.connection-timeout 只限制建立连接所需时间,不限制一次完整的对象操作。

oss.api-call-timeout 是一次 API 调用包含所有重试和重试间隔的总时限;oss.api-call-attempt-timeout 是单次尝试的时限,必须小于 或等于调用总时限。大对象或低带宽环境应根据实际传输时间调高这两个值。

客户端默认启用 multipart。对象达到 oss.multipart-threshold-in-mb 后进入 multipart 处理,单个分片不小于 oss.part-size-in-mb。该配置也作为分片服务端复制的基础分片大小;当对象过大时会自动增大分片,确保不超过 10,000 个分片。 分片大小必须位于 5–5120 MB。请求校验和计算与响应校验均使用 WHEN_REQUIRED,避免对不要求 checksum 的 S3 兼容服务改变协议行为。

Spring 容器销毁 OssTemplate 时会依次关闭 Presigner、Transfer Manager 和 S3 client。纯 Java 场景应在应用停止时调用 OssTemplate.stop();即使某个资源关闭失败,其余资源仍会继续释放。

存储类型

oss.type 对象存储 URL 拼接方式
minio MinIO 或本地 S3 兼容服务 Path-Style,格式为 {endpoint}/{bucket}/
cos 腾讯云 COS Virtual-Hosted,格式为 {protocol}://{bucket}.{host}/
obs 华为云 OBS Virtual-Hosted,格式为 {protocol}://{bucket}.{host}/
其他值或空值 通用 S3 兼容服务 Path-Style 兜底

底层 S3 客户端目前只在 oss.type=minio 时强制 Path-Style 寻址。使用其他 S3 兼容服务时,请先在目标环境验证 endpoint 与 Bucket 寻址方式。

Java API 用法

所有操作都从 OssTemplate 进入。

入口 能力
ossTemplate.put() 上传文件、创建目录占位、复制、移动、分片上传
ossTemplate.query() 连接测试、对象元数据、列表、树形结构、下载、预览、按前缀流式 ZIP 导出
ossTemplate.delete() 单个删除、批量删除、目录递归删除
ossTemplate.unzip() ZIP 流式解压、跨 Bucket 解压、按条目前缀过滤解压
ossTemplate.presign() 生成 GET/PUT 预签名 URL
ossTemplate.tagging() 对象标签和 Bucket 标签
ossTemplate.bucket() Bucket 详情、ACL、版本控制、时间回滚、生命周期、CORS、策略、安全配置

上传示例:

@RestController
@RequiredArgsConstructor
public class FileController {

    private final OssTemplate ossTemplate;

    @PostMapping("/upload")
    public String upload(@RequestParam("file") MultipartFile file) throws IOException {
        try (InputStream inputStream = file.getInputStream()) {
            ObjectInfo objectInfo = ossTemplate.put()
                    .putObject("uploads/", file.getOriginalFilename(), inputStream);
            return objectInfo.getUrl();
        }
    }
}

对象命令上传示例:

Map<String, String> metadata = Collections.singletonMap("source", "archive-job");
Map<String, String> tags = Collections.singletonMap("environment", "production");

try (InputStream inputStream = Files.newInputStream(file)) {
    PutObjectCommand command = new PutObjectCommand(
            "archive-bucket",
            "uploads/demo.bin",
            inputStream,
            Files.size(file),
            "application/octet-stream",
            metadata,
            tags,
            checksumSha256,
            false
    );
    StoredObject storedObject = ossTemplate.put().putObject(command);
}

checksumSha256 使用 Base64 编码的 SHA-256。已知长度且带有该值的对象在 5,000,000,000 字节以内会绕过 TransferManager,使用单请求上传;不超过 oss.multipart-threshold-in-mb 的对象会发送该完整对象 checksum,超过阈值的对象不会发送预计算的完整对象 checksum。未知长度或超过单请求上限的对象使用 multipart,客户端不会发送预计算的完整对象 checksum。需要保留完整性摘要时,可将 SHA-256 保存到对象 metadata。未知长度流上传时将 contentLength 设为 null,客户端会根据 multipart 配置流式处理。 createOnly=true 会发送 If-None-Match: *,其原子条件写语义取决于目标 S3 兼容服务是否支持。

复制与元数据查询示例:

StoredObject copied = ossTemplate.put().copyObject(new CopyObjectCommand(
        "source-bucket",
        "uploads/demo.bin",
        "archive-bucket",
        "archive/demo.bin"
));

StoredObject metadata = ossTemplate.query().headObject(
        "archive-bucket",
        "archive/demo.bin"
);

复制在当前客户端可访问的 Bucket 之间由对象存储服务端完成,不经过应用进程中转。源对象不超过 5 GB 时使用单次 CopyObject;超过 5 GB 时自动切换为 multipart upload 和 UploadPartCopy。基础分片大小取自 oss.part-size-in-mb, 并在必要时自动增大以限制在 10,000 个分片内。分片按受控窗口并发提交,并发度为 min(oss.max-connections, 8)。 分片复制会保留源对象的常用 HTTP 元数据、自定义元数据和标签;当前并发窗口全部结束后才会进入下一批,任一分片或完成阶段失败时会中止 multipart upload。 同一窗口内有多个分片失败时,第一个失败作为主异常,其余失败通过 suppressed exception 保留。等待分片期间线程被中断时, 实现会先等待当前窗口的在途请求全部结束,再恢复中断标记并中止 multipart upload,避免 abort 与仍在执行的分片请求竞态。 复制当前版本时源对象与目标对象不能完全相同,且当前实现不支持跨 endpoint 服务端复制。对象大小超过约 48.8 TiB 时会显式拒绝。 CopyObjectCommand 的五参数构造器可通过 sourceVersionId 固定历史版本;指定版本时允许复制回同一 Bucket/key, 用于把历史版本恢复为新的当前版本。复制请求已经完成但目标 HEAD 或大小校验失败时抛出 OBJECT_COPY_COMPLETED_VERIFY_FAILED,调用方应把目标状态视为“已复制但尚未确认”,不能直接按未写入重试。

复制链路会在 DEBUG 级别记录执行计划,并在 INFO/WARN 级别记录完成或失败结果。日志包含源/目标、对象大小、复制策略、 分片数、分片大小、并发度、耗时和领域错误码,可用于定位大对象复制性能与失败阶段。

受限流式读取示例:

try (InputStream inputStream = ossTemplate.query().getInputStream(
        new ReadObjectRangeCommand(
                "archive-bucket",
                "archive/demo.bin",
                1024L,
                4096L))) {
    // 从偏移量 1024 开始,最多读取 4096 字节。
}

ReadObjectRangeCommand 使用 offset + length 表达单个字节区间,并在请求前校验负数、零长度和 long 溢出。 bucket 为空时使用 OssQueryService.getDefaultBucketName() 返回的默认 Bucket;内置实现返回 oss.bucket-name,第三方实现如需支持 该语义应覆盖默认 Bucket 方法。调用方必须关闭返回的 InputStream,以释放底层 HTTP 连接。原始 bytes=... 字符串重载仅为兼容保留,已经标记为过时。

超过 5 GB 的服务端分片复制集成测试默认不执行。连接测试 MinIO 后,可在 PowerShell 中显式启用:

$env:OSS_RUN_LARGE_COPY_TEST = "true"
mvn -pl oss-spring-boot3-web-starter -am -Dtest=PutOperationsTest -Dsurefire.failIfNoSpecifiedTests=false test

move() 保持允许覆盖目标对象的兼容语义。内部使用 .oss-staging/move/ 随机 key 完成 staging-copy-delete,校验 staging 和最终对象后再删除源对象。源对象删除使用最初读取到的 ETag 作为 If-Match 条件;源在移动期间被覆盖时抛出 MOVE_SOURCE_CHANGED 并保留新源对象。兼容服务不支持条件删除时抛出 MOVE_CONDITIONAL_DELETE_UNSUPPORTED, 此时目标可能已经写入,但源对象仍会保留。

查询与预签名示例:

ObjectInfo objectInfo = ossTemplate.query().getObjectInfo("uploads/demo.txt");

boolean exists = ossTemplate.query().checkExist("uploads/demo.txt");

String downloadUrl = ossTemplate.presign()
        .generateGetPresignedUrl("uploads/demo.txt", Duration.ofMinutes(10));

checkExist() 仅在对象存储明确返回 404/NoSuchKey 时返回 false。权限不足、Bucket 不存在、网络故障和服务端错误 会抛出相应领域异常,避免把不可访问误判为不存在。目录列表、递归删除和 ZIP 目录操作使用独立的前缀规范化; release.v1 会被处理为 release.v1/,空目录前缀不允许递归删除,以防误删整个 Bucket。

MinIO 文件变化监听示例:

oss:
  enable: true
  type: minio
  bucket-name: oss-sample
  event:
    enable: true
    events:
      - s3:ObjectCreated:*
      - s3:ObjectRemoved:*
    prefix: uploads/
    reconnect-interval: 5s
@Component
public class ObjectEventHandler implements OssObjectEventListener {

    @Override
    public void onObjectChanged(OssObjectEvent event) {
        String bucketName = event.getBucketName();
        String objectKey = event.getObjectKey();
        String eventName = event.getEventName();
    }
}

该能力只在 oss.type=minio 时生效,使用 MinIO 扩展监听接口,不引入 MinIO SDK 和消息队列。事件可能重复、延迟或在断线重连期间丢失,业务侧处理应保持幂等。

文件夹压缩下载示例:

@GetMapping("/files/folder/download")
public void downloadFolder(
        @RequestParam String path,
        @RequestParam(required = false) String filename,
        HttpServletResponse response) throws IOException {
    String zipFilename = (filename == null || filename.trim().isEmpty())
            ? "folder-download.zip"
            : (filename.endsWith(".zip") ? filename : filename + ".zip");

    response.setContentType("application/zip");
    response.setHeader("Content-Disposition", "attachment; filename=\"" + zipFilename + "\"");
    ossTemplate.query().writeFolderAsZip(path, response.getOutputStream());
}

说明:

  • path 表示 S3 prefix,而不是真实文件夹。
  • 目录占位对象不会写入 ZIP,只有 prefix 下的真实对象会按相对路径进入压缩包。
  • 当前缀为空、prefix 下没有真实对象,或对象流读取失败时,接口会显式失败,不返回空 ZIP。

更多示例参考:samples/README.md 或 JavaxOpenApiOssControllerSupport.java

前端示例工程已补充“文件夹压缩下载”页面,后端示例工程已补充 /api/files/folder/download 自定义控制器用法。

内置 REST 接口

启用条件:

  • 引入对应版本的 Web Starter 或 OpenAPI Starter。
  • 宿主应用是 Spring Web 应用。
  • 配置 oss.enable=true。
  • 配置 oss.http.enable=true。

所有路径都以 ${oss.http.prefix}/oss 为前缀。示例配置 oss.http.prefix=/api 时,完整前缀为 /api/oss。

除预览、单对象下载和文件夹压缩下载接口直接输出二进制流外,其他接口统一返回 OssResponse。

分片上传

方法 路径 说明
POST /multipart/init 初始化分片上传任务
POST /multipart/chunk 上传单个分片
POST /multipart/merge 校验并合并分片
GET /multipart/parts 查询已上传分片列表

/multipart/merge 的 JSON 请求必须同时包含:

{
  "filename": "large.bin",
  "path": "uploads",
  "uploadId": "upload-id",
  "guid": "client-task-id",
  "expectedPartCount": 1,
  "expectedSize": 73400320,
  "chunkTargetList": [
    {"partNumber": 1, "etag": "etag-1"}
  ]
}

合并前会读取服务端真实分片清单,校验分片数量、连续编号、ETag 和总大小。缺少任一分片、客户端与服务端 ETag 不一致或总大小不等于 expectedSize 时不会调用 CompleteMultipartUpload。

文件上传与删除

方法 路径 说明
POST /object 上传单个文件
POST /folder 创建目录占位对象
DELETE /object 删除单个对象
DELETE /objects 批量删除对象
DELETE /folder 递归删除目录

文件查询

方法 路径 说明
GET /object 查询对象元数据
GET /object/exists 检查对象是否存在
GET /object/list 递归列举对象
GET /object/list/next-level 列举下一层级文件和目录
GET /object/list/lazy 游标分页懒加载列表
GET /object/tree 获取目录树
GET /object/tree/search 按关键字搜索目录树
GET /object/tree/folder 获取仅包含目录的树
GET /buckets 列举当前凭证可见的 Bucket
GET /buckets/{bucketName} 查询 Bucket 聚合详情
GET /buckets/{bucketName}/access 查询 Bucket ACL
PUT /buckets/{bucketName}/access 设置 Bucket ACL
POST /buckets/{bucketName}/rewind 按时间回滚 Bucket 当前可见状态
GET /connect 测试默认 Bucket 连通性

预览、下载、复制、移动

方法 路径 说明
GET /object/preview/** 内联预览对象,支持 HTTP Range
GET /object/download/** 以附件方式下载对象,支持 HTTP Range
GET /folder/download 按 path 指定的 prefix 流式压缩下载 ZIP
POST /object/copy 复制对象
POST /object/move 移动对象到目标目录

/folder/download 参数说明:

  • path:必填,表示 S3 prefix。
  • filename:可选,自定义 ZIP 文件名;未传时默认使用 prefix 最后一级名称。

该接口不会把目录占位对象写入 ZIP。若 path 为空、prefix 下没有真实对象,或对象流读取失败,会直接返回错误响应,不返回空 ZIP。

解压与预签名

方法 路径 说明
POST /unzip 在默认 Bucket 内流式解压 ZIP
POST /unzip/cross-bucket 跨 Bucket 流式解压 ZIP
POST /unzip/filter 按条目前缀过滤解压
GET /presign/get 生成下载预签名 URL
GET /presign/put 生成上传预签名 URL

流式解压拒绝绝对路径、Windows 盘符和 ./.. 路径段,并使用以下安全上限:最多 10,000 个条目、 单条目最多 5 GiB、一次解压累计最多 50 GiB。达到上限时抛出 UNZIP_LIMIT_EXCEEDED,不会继续创建后续对象。

Bucket 时间回滚仅允许在版本控制状态为 Enabled 时执行。恢复历史版本复用自动单次/分片复制链路, 因此历史对象超过 5 GB 时会自动使用 multipart copy;Suspended 状态会被明确拒绝。

标签与 Bucket 管理

方法 路径 说明
GET /object/tags 获取对象标签
PUT /object/tags 覆盖设置对象标签
PATCH /object/tags 合并更新对象标签
DELETE /object/tags 删除对象标签
POST /bucket 创建 Bucket
GET /bucket/versioning 查询版本控制状态
PUT /bucket/versioning/enable 启用版本控制
PUT /bucket/versioning/suspend 挂起版本控制
GET /bucket/lifecycle 查询生命周期规则
POST /bucket/lifecycle/expiration 添加过期删除规则
DELETE /bucket/lifecycle 删除生命周期规则
GET /bucket/cors 查询 CORS 配置
PUT /bucket/cors/allow-all 设置允许所有来源的 CORS
DELETE /bucket/cors 删除 CORS 配置
GET /bucket/policy 查询 Bucket 策略
PUT /bucket/policy 设置 Bucket 策略
DELETE /bucket/policy 删除 Bucket 策略
PUT /bucket/encryption/enable 启用 SSE-S3 服务端加密
PUT /bucket/public-access/block 开启公共访问屏蔽
GET /bucket/tags 获取 Bucket 标签
PUT /bucket/tags 覆盖设置 Bucket 标签
DELETE /bucket/tags 删除 Bucket 标签

许可证

Apache License 2.0

About

oss-spring-boot-starter is a toolkit based on Spring Boot and Amazon S3. With this toolkit, you can easily perform operations related to OSS (Object Storage Service) storage. 基于springboot的对象存储工具。

Topics

Resources

Stars

9 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages