Skip to content

feat(server/s3): support multipart upload#2813

Draft
xrgzs wants to merge 3 commits into
mainfrom
feat/s3-multipart
Draft

feat(server/s3): support multipart upload#2813
xrgzs wants to merge 3 commits into
mainfrom
feat/s3-multipart

Conversation

@xrgzs

@xrgzs xrgzs commented Jul 21, 2026

Copy link
Copy Markdown
Member

Summary / 摘要

为 fake S3 服务器实现分片上传(multipart upload)支持,并自动回收被遗弃的分片上传。本 PR 含三个提交:

  1. refactor(server/s3): use OpenListTeam/gofakes3 -- 将依赖从 github.com/itsHenry35/gofakes3 v0.0.8 切换到 github.com/OpenListTeam/gofakes3 v0.8.0。旧版只有把所有分片缓存在内存的默认上传器;OpenListTeam fork 新增了可选的 MultipartBackend 接口,允许后端自行流式处理分片,这是实现分片上传的前提。同步更新 server/s3 下 8 个文件的 import 路径。
  2. feat(server/s3): support multipart upload -- 在 s3Backend 上实现 gofakes3.MultipartBackend,把分片上传四个操作(Initiate / UploadPart / Complete / Abort)路由到 OpenList 自有后端:每个分片流式写入本地临时文件,完成时按分片号顺序拼合并写入底层存储。
  3. feat(server/s3): reap abandoned multipart uploads -- 回收被遗弃的分片上传,避免临时文件无限累积。
  • 为什么需要:此前分片上传回退到 gofakes3 默认的内存上传器,会把每个分片都缓存在内存中,大文件分片上传可能耗尽内存;且客户端不 complete/abort 的孤儿上传会一直占用磁盘。现在改为按分片落盘(内存占用恒定),并由后台 reaper 自动清理超时未活动的上传。

  • 用户可感知的行为变化:S3 客户端(awscli / s3cmd / rclone 等)的分片上传现在可用,不再因内存限制而失败;被遗弃的分片上传会在 TTL 后被自动回收。

  • 重要实现变化:

    • 依赖切换到 OpenListTeam/gofakes3 v0.8.0(base Backend 接口兼容,既有单分片 PutObject 等行为不变)
    • 新增 server/s3/multipart.go,实现 gofakes3.MultipartBackend(CreateMultipartUpload / UploadPart / CompleteMultipartUpload / AbortMultipartUpload)
    • s3Backend 新增 uploads sync.Map 跟踪进行中的上传;每个上传记录 lastActivity,在创建与每次分片上传时更新
    • PutObject 主体抽取为可复用的 putStream,与分片 Complete 路径共享(目录创建、元数据、忽略规则一致);单分片 PutObject 行为不变
    • 分片临时文件存放于 conf.Conf.TempDir 下的 s3-multipart-* 目录;返回 S3 规范的分片 etag("<md5ofmd5s>-N")
    • 校验分片升序、etag 匹配、分片存在;短读(实际字节少于声明 Content-Length)返回 ErrIncompleteBody;abort 幂等;Complete 失败时保留上传以便客户端重试
    • 后台 reaper:每个 backend 实例启动一个协程,按 TTL/4(钳制 [10s, 1h])间隔回收 lastActivity 超过 TTL 的上传及其临时目录;启动时额外清理上次进程崩溃残留的 s3-multipart-* 目录(仅清理超过 TTL 的,避免误伤并发启动的兄弟实例)
    • 新增可配置 s3.multipart_ttl(env S3_MULTIPART_TTL),用 time.ParseDuration 解析,空/非法/<=0 回退默认 24h
  • 配置/存储/API/兼容性:新增一个可选配置项 s3.multipart_ttl(默认 24h,不配置即用默认值);不修改已有 API、存储格式或迁移行为。

  • This PR has breaking changes.
    / 此 PR 包含破坏性变更。

  • This PR changes public API, config, storage format, or migration behavior.
    / 此 PR 修改了公开 API、配置、存储格式或迁移行为。

  • This PR requires corresponding changes in related repositories.
    / 此 PR 需要关联仓库同步修改。

Related repository PRs / 关联仓库 PR:

  • OpenList-Frontend: 无
  • OpenList-Docs: 无

Related Issues / 关联 Issue

Related to #460

Testing / 测试

  • go test ./...
  • Manual test / 手动测试:

执行过的命令与结果(平台:macOS arm64,Go 1.26.5):

  • go build ./server/s3/go build ./server/go build ./internal/conf/ -- 通过
  • go vet ./server/...go vet ./internal/conf/ -- 干净
  • gofmt -l server/s3/*.go internal/conf/config.go -- 干净
  • go test ./server/s3/ -count=1 -- 通过,包含新增测试:
    • TestMultipartUploadEndToEnd:在真实 Local 驱动上 create->3 分片->complete,校验落盘文件内容为拼接结果、etag 为 "<hex>-3";同号重传覆盖;短读->ErrIncompleteBody;未知 upload->ErrNoSuchUpload;越界分片号->ErrInvalidPart;乱序->ErrInvalidPartOrder;错 etag->ErrInvalidPart;缺分片->ErrInvalidPart;失败的 complete 保留上传可重试
    • TestMultipartAbort:abort 删除临时目录并移除记录,且对未知 upload 幂等
    • TestMultipartReapExpired:lastActivity 超过 TTL 的上传被回收且临时目录删除,活跃上传保留
    • TestMultipartCleanupStaleDirs:超过 TTL 的 s3-multipart-* 残留目录被清理,新目录保留
  • go test ./...:server/s3 通过。其余失败均与本次改动无关且为环境/既有问题--
    • pkg/aria2/rpc:需连接 localhost:6800 的 aria2 守护进程(本机禁网)
    • drivers/teldrivedrivers/webdav:需外部服务器/凭据
    • internal/net TestNewOSSClientUsesEnvironmentHTTPSProxy:仓库既有失败(expected *http.Transport, got *net.safeTransport)
    • 部分 drivers/*internal/offline_download/* 的 build failed:并行 go test ./... 时模块缓存写入竞争所致,单独 go build 正常
    • 上述包均不导入 server/s3(仅 server/s3.go 导入),不受本次改动影响

未执行:对运行中的 OpenList 实例用 awscli / rclone 做手动联调(如维护者需要可补)。

Checklist / 检查清单

  • I have read CONTRIBUTING.
    / 我已阅读 CONTRIBUTING
  • I confirm this contribution follows the repository license, contribution policy, and code of conduct.
    / 我确认此贡献符合仓库许可证、贡献规范和行为准则。
  • I have formatted the changed code with gofmt, go fmt, or prettier where applicable.
    / 我已按适用情况使用 gofmtgo fmtprettier 格式化变更代码。
  • I have requested review from relevant maintainers or code owners where applicable.
    / 我已在适用情况下请求相关维护者或代码所有者审查。

AI Disclosure / AI 使用声明

  • This PR includes AI-assisted content.
    / 此 PR 包含 AI 辅助内容。

Tools used / 使用工具:

  • ChatGPT
  • Codex + GLM 5.2
  • GitHub Copilot
  • Claude
  • Gemini
  • Other (please specify) / 其他(请注明):

Usage scope / 使用范围:

  • Code generation / 代码生成

  • Refactoring / 重构

  • Documentation / 文档

  • Tests / 测试

  • Translation / 翻译

  • Review assistance / 审查辅助

  • I have reviewed and validated all AI-assisted content included in this PR.
    / 我已审核并验证此 PR 中的所有 AI 辅助内容。

  • I have ensured that all AI-assisted commits include Co-Authored-By attribution.
    / 我已确保所有 AI 辅助提交都包含 Co-Authored-By 归属信息。

  • I can reproduce all AI-assisted content included in this PR without any AI tools.
    / 我可以在没有任何 AI 工具的情况下重现此 PR 中包含的所有 AI 辅助内容。

Known Limitations / 已知限制

  • 分片临时文件写入 conf.Conf.TempDir,大文件上传会占用等量本地磁盘(相对原内存缓冲已是改进,为流式后端的固有取舍)。
  • TTL 内的崩溃残留目录会在下次启动且超过 TTL 后才被清理(默认 24h);正常运行期间的孤儿上传由后台 reaper 在 TTL 后自动回收。

xrgzs and others added 3 commits July 21, 2026 00:23
Replace itsHenry35/gofakes3 with OpenListTeam/gofakes3

Signed-off-by: MadDogOwner <xiaoran@xrgzs.top>
- Implement gofakes3 MultipartBackend (Create/UploadPart/Complete/Abort)
  on s3Backend so multipart parts stream to local temp files instead of
  being buffered in memory
- Track in-progress uploads via a new uploads sync.Map on s3Backend
- Refactor the PutObject body into a reusable putStream helper shared with
  the multipart Complete path
- Validate part ordering, etags and existence, reject short reads, and
  return S3-style multipart etags
- Add end-to-end multipart tests against a real Local driver

Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
- Track lastActivity on each multipart upload, updated on create and
  every part upload, so idle uploads can be detected
- Add a background reaper per backend instance that removes uploads
  inactive for longer than the TTL (default 24h) and cleans their temp
  directories
- Add a startup sweep that removes leftover s3-multipart-* directories
  older than the TTL, recovering part files from a previous crash
- Add a configurable s3.multipart_ttl (env S3_MULTIPART_TTL) duration,
  parsed via time.ParseDuration with a 24h default
- Add tests for TTL-based reaping and stale-directory cleanup

Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
@xrgzs xrgzs added WIP An Issue already has a PR to fix Module: Server API and protocol changes Module: Stream Transmission optimization and file stream handling-related features ecosystem labels Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ecosystem Module: Server API and protocol changes Module: Stream Transmission optimization and file stream handling-related features WIP An Issue already has a PR to fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant