Skip to content

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

Open
xrgzs wants to merge 6 commits into
mainfrom
feat/s3-multipart
Open

feat(server/s3): support multipart upload#2813
xrgzs wants to merge 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 且同步了上游安全修复,分片落盘方案合理,无阻塞项。

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.

3 participants