Skip to content

feat: 文件管理器上传/下载 + 备份下载 - #1

Merged
SMNETSTUDIO merged 1 commit into
mainfrom
feat/file-upload-download
Aug 22, 2026
Merged

feat: 文件管理器上传/下载 + 备份下载#1
SMNETSTUDIO merged 1 commit into
mainfrom
feat/file-upload-download

Conversation

@SMNETSTUDIO

Copy link
Copy Markdown
Owner

做了什么

文件管理器 — 上传

  • POST /api/instances/:iid/files/upload?path=&name=&overwrite=,body 就是文件原始字节,没有引入新依赖(项目原本只有 express,multipart 解析要额外装包)
  • 先写 .mcsp-upload-<rand> 临时文件再 rename,上传中断不会在目录里留半截文件
  • 文件名必须单段(禁 / \ . .. 与控制字符),目录复用原有 safePath 沙箱
  • 大小上限 MCSP_MAX_UPLOAD_MB(默认 2048):Content-Length 预检 + 流中途超限即断连回 413
  • 同名默认 409,需显式 overwrite=1
  • 前端:工具栏「⬆ 上传」(多选)+ 拖拽上传,逐文件实时进度条;同名在开传前一次性确认,不会传了几百 MB 才卡在 409

文件管理器 — 下载

  • GET /api/instances/:iid/files/download?path=
  • 文件:res.download 原样回传,不限扩展名,支持 Range 断点续传
  • 目录:tar czf - 流式打包,不落盘;客户端取消即 SIGKILL 掉 tar(否则一个大世界会白烧几分钟 CPU)
  • 实例根目录不给下载 → 引导到「备份」页(那条路会先 save-all)

备份管理 — 下载

  • GET /api/instances/:iid/backups/:id/download,流式回传 tar.gz
  • listBackupssize 字节字段,前端改用 fmtSize —— 原来固定按 GB 显示,小备份一律是 0.00 GB

顺带修复的路径穿越

/backups/:idrestore / delete 原本只检查 endsWith('.tar.gz')。Express 会解码 :id,所以 ..%2F..%2F..%2Fetc%2Fpasswd.tar.gz 能带着 path.join 走出 backups/ 目录。三处改为共用 ^[\w.-]+\.tar\.gz$ 校验。

验证

npm test 26/26 通过(从 21 项加了 5 项:上传文件名/路径沙箱、下载路径沙箱、下载实例根目录拒绝、备份 id 沙箱)。

脚本层另外验过:

  • 250 KB / 500 KB 二进制上传后下载回来 Buffer.equals 完全一致
  • 中文文件名响应头正确带 filename*=UTF-8''...
  • 目录打包:世界目录/{level.dat, 说明.txt, region/r.0.0.mca} 解开后内容与源文件逐字节一致;空目录也正常
  • 上限 413 的两条路径(Content-Length 预检 + chunked 中途掐断),临时文件无残留
  • 取消测试:400 MB 不可压缩目录,读 196 KB 后 abort,pgrep tar 无残留
  • 穿越 400 / 根目录 400 / 不存在 404 / 未登录 401 / Range 206

浏览器实测:单文件、3 文件批量、覆盖确认(接受/取消)、超限失败 UI、目录与文件下载按钮均不会误触发行点击(打开编辑器/进入目录)。

已知限制

目录下载没有 Content-Length(边压边传决定的),浏览器进度条显示「未知大小」。要准确进度就得先压到磁盘再传,代价是大目录多占一份临时空间且首字节延迟高得多。

🤖 Generated with Claude Code

文件管理器:
- 上传:POST /files/upload,body 即文件原始字节,不引 multipart 依赖。
  先写 .mcsp-upload-* 临时文件再 rename,中断不留半截文件;文件名必须单段,
  超过 MCSP_MAX_UPLOAD_MB(默认 2048)立即断流回 413。
  前端支持多选与拖拽,逐文件进度条,同名在开传前一次性确认。
- 下载:GET /files/download。文件走 res.download 原样回传(支持 Range);
  目录现 tar czf - 流式打包,不落盘,客户端断开即 SIGKILL 掉 tar。
  实例根目录不给下载,那是「备份」的活(会先 save-all)。

备份管理:
- 新增 GET /backups/:id/download,流式回传 tar.gz。
- listBackups 补 size 字节字段,前端改用 fmtSize —— 原来固定按 GB 显示,
  小备份一律是 0.00 GB。

顺带修复路径穿越:/backups/:id 的 restore/delete 原本只检查 endsWith('.tar.gz'),
而 Express 会解码 :id,`..%2F..%2F` 能带着 path.join 走出 backups/ 目录。
三处改为共用 ^[\w.-]+\.tar\.gz$ 校验。

smoke 从 21 项加到 26 项(上传文件名/路径沙箱、下载路径沙箱、
下载实例根目录拒绝、备份 id 沙箱)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@SMNETSTUDIO
SMNETSTUDIO merged commit e8e789e into main Aug 22, 2026
2 checks passed
@SMNETSTUDIO
SMNETSTUDIO deleted the feat/file-upload-download branch August 22, 2026 06:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant