00 学习路线与贯穿项目
1. 为什么文件处理比 CRUD 难
数据库 CRUD 通常由驱动管理连接和事务,文件流程却同时涉及路径、权限、磁盘空间、句柄、Stream、临时目录和操作系统差异。一次 Markdown ZIP 转换至少拥有三种资源:
输入 ZIP + 解压目录 + 输出 ZIP
任意阶段都可能失败,失败后还要回答:输出是否完整、句柄是否关闭、临时文件是否删除、客户端是否已经收到响应头。
2. 贯穿链路
HTTP 上传
→ Multer 临时文件
→ 创建任务目录
→ 校验 ZIP Entry
→ 解压 Markdown 和资源
→ 渲染并清理 HTML
→ Archiver 生成结果 ZIP
→ Stream 下载
→ 清理临时目录
各层职责:
| 层 | 主要职责 |
|---|---|
| Express/Multer | HTTP 输入、上传大小和数量限制 |
path |
构造、解析和比较路径 |
fs |
文件、目录和句柄操作 |
| Stream | 有界缓冲的数据传输 |
| yauzl | 读取 ZIP 目录和 Entry 数据 |
| archiver | 生成 ZIP |
| Service | 状态、清理、错误映射和审计 |
3. 两套 Stream 不要混淆
本系列重点是 Node.js Stream:
import type { Readable, Writable } from "node:stream";
浏览器 Fetch 使用的通常是 Web Streams:
ReadableStream<Uint8Array>
Node.js 22 同时支持两套模型,也提供转换接口,但它们的事件和消费方式不同。本教程出现 data、end、finish、pipe() 时,指 Node.js Stream。
4. 练习目录约定
建议单独创建:
playground/fs-path-stream/
├─input/
├─work/
└─output/
不要用真实上传目录练习删除。每个练习都应先打印将要操作的绝对路径。
5. 错误分类
贯穿系列使用三类错误:
用户数据错误:非法 ZIP、路径穿越、文件过大
环境错误:权限不足、磁盘满、句柄耗尽
程序错误:重复回调、遗漏 await、错误事件未监听
公开响应不应暴露服务器绝对路径;日志可以保存 taskId、相对路径、错误码和阶段。
6. 每章验收方式
除了正常输入,还要测试:
- 输入文件不存在;
- 输出目录无权限;
- 中途主动
abort; - 多个文件同名;
- 文件数量过多;
- ZIP 中出现
../; - 目标文件已存在;
- 客户端下载到一半断开。
练习题
- 画出当前 Markdown ZIP 接口中所有临时文件的生命周期。
- 标出每一步可能占用的句柄、内存和磁盘空间。
- 解释为什么 HTTP 超时不能自动停止后台文件写入。
- 给用户输入错误和系统错误分别设计三个业务码。
完成标准
- 能说出
path、fs、Stream 和 ZIP 库的职责边界。 - 能区分 Node.js Stream 与 Web Stream。
- 在编写代码前先设计失败清理路径。