新增“上传文件夹”入口,选择目录后先在浏览器端打包为 zip, 上传完成后自动触发远端解压并尝试清理临时压缩包。 同时重排文件右键菜单,补齐终端子菜单、复制文件名与绝对路径等操作, 并扩展上传任务状态展示。 同步前后端包版本到 1.0.0,并将设置页版本显示规范为 1.0
6.4 KiB
变更提案: folder-upload-auto-zip
元信息
类型: 功能调整
方案类型: implementation
优先级: P1
状态: 已完成
状态说明: 已补齐文件夹选择、浏览器端 zip 上传与远端自动解压,并通过前端构建验证
创建: 2026-03-26
1. 需求
背景
当前文件管理器只支持普通文件上传。虽然拖拽目录时已经能递归遍历并逐个上传文件,但面对大量小文件目录时,浏览器侧扫描、前端逐文件分块、后端逐文件创建与远端 SFTP 写入都会显著拉长整体耗时,用户体感接近“卡住”。
目标
- 在现有文件管理器中新增明确的“上传文件夹”入口。
- 选择文件夹后,在浏览器端先将目录树压缩为单个 zip,再复用现有上传链路。
- 上传成功后自动调用远端解压,尽量让用户获得“像直接上传文件夹”的结果。
- 普通文件上传保持现有行为,不影响已有入口和拖拽目录上传兼容性。
约束条件
范围约束: 优先复用现有 sftp:upload 与 sftp:decompress,不新增 REST 上传接口
前端约束: 需要在浏览器端完成目录树收集和 zip 构建
后端约束: 远端自动解压依赖现有服务器命令检测与解压实现
兼容约束: 普通文件上传、现有拖拽上传和文件树刷新逻辑不能回归
验收标准
- 文件管理器出现独立的“上传文件夹”入口
- 选择文件夹后会先压缩为 zip,再作为单次上传任务发送
- zip 上传成功后会自动触发远端解压
- 普通文件上传行为保持不变
- 前后端构建通过
2. 方案
技术方案
在 FileManager.vue 中增加第二个隐藏目录选择 input,并将“上传文件”与“上传文件夹”拆分为两个明确入口。前端新增目录压缩逻辑:利用 webkitdirectory 返回的 FileList 生成目录树,并通过前端 zip 库构建一个临时 File/Blob。上传层继续走 useFileUploader 的 sftp:upload:start/chunk 协议,但补充目录压缩任务元数据、成功回调和自动解压回调;上传成功后复用 useSftpActions 现有 decompressItem() 能力,并在成功后清理临时 zip。
影响范围
涉及模块:
- frontend: FileManager.vue、useFileUploader.ts、upload.types.ts、locales
- backend: 无协议重构,最多仅需适配现有上传/解压消息的边界处理
预计变更文件: 6-10
风险评估
| 风险 | 等级 | 应对 |
|---|---|---|
| 浏览器端压缩大目录时主线程占用明显 | 中 | 在 UI 中明确显示“压缩中”,并只对显式文件夹入口启用 |
| 远端缺少 unzip 或 tar 命令时自动解压失败 | 中 | 继续复用现有 sftp:command_not_found 提示,上传成功但解压失败时给出明确报错 |
| 自动解压后再清理临时 zip 可能因当前目录变更导致删除目标错误 | 中 | 删除逻辑基于上传完成返回的绝对远端路径,而不是依赖当前 UI 路径 |
| 目录名与 zip 临时文件名冲突 | 低 | 为临时 zip 文件名附加固定后缀,避免覆盖现有同名目录/文件 |
3. 技术设计
架构设计
flowchart TD
A[FileManager 文件夹选择] --> B[浏览器收集 FileList]
B --> C[前端生成 zip Blob/File]
C --> D[useFileUploader 发起 sftp:upload]
D --> E[后端 SftpService 写入远端 zip]
E --> F[上传成功回调]
F --> G[useSftpActions.decompressItem]
G --> H[远端解压并刷新文件树]
H --> I[清理临时 zip]
API设计
本轮不新增独立 HTTP API,沿用现有 WebSocket 消息:
sftp:upload:startsftp:upload:chunksftp:upload:successsftp:decompresssftp:decompress:success
前端本地新增的只是上传任务元数据,不改动后端消息协议主体。
数据模型
| 字段 | 类型 | 说明 |
|---|---|---|
mode |
'file' | 'folder-archive' |
上传任务模式,区分普通文件与目录压缩上传 |
archiveFileName |
string |
浏览器端生成的临时 zip 文件名 |
remoteArchivePath |
string |
远端临时 zip 的绝对路径 |
decompressAfterUpload |
boolean |
上传成功后是否自动触发解压 |
cleanupArchiveAfterExtract |
boolean |
解压成功后是否自动删除临时 zip |
4. 核心场景
场景: 上传文件夹并自动解压
模块: frontend
条件: 用户在文件管理器点击“上传文件夹”,并选择一个本地目录。
行为: 前端将目录内容打包为 zip,上传到当前远端目录,成功后自动调用解压并清理临时 zip。
结果: 远端目录出现解压后的文件夹内容,用户无需手工上传 zip 再解压。
场景: 远端缺少解压命令
模块: frontend / backend
条件: zip 上传成功,但服务器上没有可用解压命令。
行为: 复用现有 sftp:command_not_found / sftp:decompress:error 错误反馈。
结果: 用户能看到“上传成功但自动解压失败”的明确信号,并保留上传的 zip 文件用于手工处理。
5. 技术决策
folder-upload-auto-zip#D001: 文件夹上传采用“前端压缩 + 现有上传协议 + 远端自动解压”
日期: 2026-03-26
状态: ✅采纳
背景: 现有目录上传是逐文件递归上传,小文件多时开销大;而后端已经具备远端解压能力。
选项分析:
| 选项 | 优点 | 缺点 |
|---|---|---|
| A: 保持逐文件目录上传 | 不引入新依赖,链路简单 | 小文件多时扫描与上传时间长,用户体感差 |
| B: 前端压缩成 zip 后上传,再自动解压 | 最大化复用现有协议,明显减少小文件上传请求数 | 前端需要额外压缩逻辑,浏览器端会有打包耗时 |
| C: 后端接收目录流并服务端压缩/展开 | 可把压缩开销从浏览器移走 | 需要重写上传协议与后端缓存链路,改动面过大 |
| 决策: 选择方案 B | ||
理由: 在当前仓库里,这是性能收益最大且改动最小的路径,能复用现有 sftp:upload 和 sftp:decompress,避免引入新的后端上传接口。 |
||
影响: 主要影响 packages/frontend 的文件管理器和上传状态管理,后端维持现有 WebSocket/SFTP 能力。 |
6. 成果设计
N/A。本轮是现有文件管理器能力增强,不引入新的视觉主题方向,仅延续当前工具栏和上传浮层风格。