Skip to content

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

Merged
jyxjjj merged 6 commits into
mainfrom
feat/s3-multipart
Sep 5, 2026
Merged

feat(server/s3): support multipart upload#2813
jyxjjj merged 6 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.1。旧版只有把所有分片缓存在内存的默认上传器;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.1(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: TODO

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 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
@xrgzs
xrgzs force-pushed the feat/s3-multipart branch from 9a51b73 to 48f5af8 Compare August 7, 2026 01:14
PIKACHUIM

This comment was marked as outdated.

xrgzs and others added 3 commits August 29, 2026 15:26
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 force-pushed the feat/s3-multipart branch from 48f5af8 to 59b1dfd Compare August 29, 2026 07:27
xrgzs added 2 commits August 29, 2026 15:51
Signed-off-by: MadDogOwner <xiaoran@xrgzs.top>
Signed-off-by: MadDogOwner <xiaoran@xrgzs.top>
@xrgzs
xrgzs force-pushed the feat/s3-multipart branch from e66f47b to 6ef934f Compare August 29, 2026 07:54
@xrgzs
xrgzs marked this pull request as ready for review August 29, 2026 07:54
@xrgzs xrgzs removed the WIP An Issue already has a PR to fix label Aug 29, 2026
Signed-off-by: MadDogOwner <xiaoran@xrgzs.top>

@pikachuren pikachuren left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙏 感谢 @xrgzs 提交!
🤖 AI 自动审核声明:本评审报告由 AI 自动生成,当前使用 Claude Opus 5 模型进行分析。
⚠️ AI 分析结果仅供参考,可能存在误判或遗漏。如您发现任何问题或有不同意见,欢迎随时提出讨论和纠正。
⚠️ 重要提醒:即使 AI 评审认为代码质量良好且建议合并,最终是否合并仍需由项目维护者进行人工判定。项目维护者会综合考虑代码质量、项目规划、技术方向、团队资源等多方面因素做出决策。

🎯 结论

✅ 建议 Approve(由维护者人工确认)— 依赖迁移来源可信,分片落盘方案解决了真实的内存问题

📖 概要

feat(server/s3): support multipart upload · 为内置 S3 服务端增加分片上传支持。
核心改动:依赖从 itsHenry35/gofakes3 迁移到 OpenListTeam/gofakes3 v0.8.1,实现 gofakes3.MultipartBackend,分片先落本地临时文件、完成时再组装写入存储;新增 MultipartTTL 配置与后台清理协程。

🧭 整体方案

技术路线是「分片落盘 + 完成时组装」而非全内存缓冲——注释里也明确写了这一动机。对于内置 S3 服务端来说这是正确取舍:客户端上传大文件时若把所有分片堆在内存,很容易 OOM。配合 startReaper() 后台清理过期的未完成上传,避免临时文件无限堆积,设计比较完整。putStreamPutObject 与 multipart complete 两条路径的目录创建、元数据、忽略规则统一起来,也避免了两套逻辑漂移。

📊 变更统计

12 个文件(+749 / -32 行) | 功能 ⭐⭐⭐⭐⭐ | 最小改动 ⭐⭐⭐ | 前向兼容 ⭐⭐⭐⭐ | 方案设计 ⭐⭐⭐⭐

🚨 关键问题

P0(阻塞合并):无

P1(建议修复):无

P2(可选)

  • 💡 MultipartTTL 是 string 类型(默认 "24h")。若用户填入无法被 time.ParseDuration 解析的值,请问会 fallback 到默认值还是导致启动失败?建议做一次显式校验并在配置错误时给出清晰提示~
  • 💡 分片临时文件落在本地磁盘,24h TTL 期间若有大量未完成上传,磁盘占用可能相当可观。是否考虑增加一个总容量上限,或在文档中提示运维预留空间?
  • 💡 uploads *sync.Map 保存上传状态,进程重启后这些状态会丢失,但磁盘上的临时文件仍在。请问 startReaper 会清理这些「孤儿」临时文件吗?如果只清理 map 中记录的项,重启后残留文件可能永远不会被回收~
  • 💡 建议补充集成测试:大文件分片上传、中途 abort、分片乱序到达、TTL 过期清理这几个场景~

🔐 依赖安全审查

  • 审查范围github.com/itsHenry35/gofakes3 v0.0.8github.com/OpenListTeam/gofakes3 v0.8.1
  • 审查方式:核查两个仓库的 fork 来源、组织归属、tag 真实性与近期提交记录
  • 安全评估:✅ 无恶意代码,来源可信
  • 详细结论
    • 新依赖 OpenListTeam/gofakes3 归属 OpenListTeam 组织(owner type = Organization),是项目自有仓库而非第三方个人账号
    • 两者同源:新旧依赖均 fork 自上游 rclone/gofakes3,不存在换用来路不明的替代包
    • v0.8.1 tag 真实存在(f3fb8106
    • 近期提交记录正常:包含上游 rclone 维护者 ncw 的安全修复(修复多实例间 auth key 共享、更新 aws-sdk-go-v2 修补 GO-2026-5764 漏洞),以及原作者 itsHenry35 的 AWS Signature V2 实现
    • 本次迁移实际上是安全性提升:从个人 fork 转为组织维护,并同步了上游的漏洞修复
    • 未发现供应链投毒迹象

📂 逐文件分析

server/s3/backend.go

改动意图:实现分片上传后端。
代码逻辑s3Backend 新增 uploads *sync.Map 跟踪上传状态;newBackend 启动 reaper;putStream 抽出共用写入逻辑供 PutObject 与 complete 复用。
问题分析:分片落盘避免内存膨胀的取舍正确,共用 putStream 保证了两条路径行为一致,未发现阻塞问题。

internal/conf/config.go

问题分析:新增 MultipartTTL 且有默认值,向后兼容良好;建议补充解析失败的处理(P2)。

go.mod / go.sum

问题分析:见上方依赖安全审查,来源可信。

✅ 待处理清单

  • [P2] 为 MultipartTTL 增加解析校验与错误提示
  • [P2] 评估临时文件总容量上限,或在文档中提示磁盘预留
  • [P2] 确认进程重启后遗留临时文件能否被 reaper 回收
  • [P2] 补充分片上传的集成测试(abort / 乱序 / TTL 过期)

🎯 结论:✅ 建议 Approve — 依赖迁移到组织自有 fork 且同步了上游安全修复,分片落盘方案合理,无阻塞项。

@jyxjjj
jyxjjj merged commit 6247cf7 into main Sep 5, 2026
12 checks passed
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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants