1. fs/promises 模块
Node.js 的 fs/promises 模块提供了基于 Promise 的文件系统 API,是现代 TypeScript 项目的首选。
import { readFile, writeFile, mkdir, readdir, stat, unlink } from "fs/promises";
// 读取文本文件
const content = await readFile("./config.json", "utf-8");
console.log(content);
// 写入文件(不存在则创建,存在则覆盖)
await writeFile("./output.txt", "Hello, TypeScript!", "utf-8");
// 创建目录(recursive 类似 mkdir -p)
await mkdir("./logs/2024", { recursive: true });
// 读取目录内容
const files = await readdir("./src");
console.log(files); // ["index.ts", "utils.ts", ...]
// 获取文件信息
const info = await stat("./package.json");
console.log(info.size, info.isFile(), info.isDirectory());
// 删除文件
await unlink("./temp.txt");
🔄 对比 Python:
readFile 类似 open().read(),readdir 类似 os.listdir(),stat 类似 os.stat()。
2. 同步 vs 异步
Node.js 的 fs 模块同时提供同步和异步版本。在服务端场景中,应始终优先使用异步 API。
import { readFileSync } from "fs";
import { readFile } from "fs/promises";
// 同步读取 —— 会阻塞事件循环
const data = readFileSync("./config.json", "utf-8");
// 异步读取 —— 不阻塞
const dataAsync = await readFile("./config.json", "utf-8");
何时使用同步 API?
- CLI 脚本或一次性工具
- 应用启动阶段加载配置文件
- 测试代码中的辅助操作
⚠️ 在 HTTP 服务器的请求处理中,绝不要使用同步 API——它会阻塞所有其他请求。
3. Stream 流式读写
处理大文件时,Stream 可以避免将整个文件加载到内存中。这和 Java 的 InputStream/OutputStream 理念一致。
import { createReadStream, createWriteStream } from "fs";
import { pipeline } from "stream/promises";
// 流式复制大文件
async function copyFile(src: string, dest: string): Promise<void> {
const readStream = createReadStream(src);
const writeStream = createWriteStream(dest);
await pipeline(readStream, writeStream);
console.log("复制完成");
}
// 逐块读取处理
const stream = createReadStream("./huge-log.txt", { encoding: "utf-8" });
let lineCount = 0;
for await (const chunk of stream) {
lineCount += (chunk as string).split("\n").length;
}
console.log(`总行数: ${lineCount}`);
何时使用 Stream?
- 文件大于 100MB
- 需要逐行/逐块处理数据
- 网络传输(HTTP 响应发送文件)
4. path 模块
永远不要手动拼接路径字符串。path 模块能正确处理不同操作系统的路径分隔符。
import path from "path";
import { fileURLToPath } from "url";
// 常用方法
path.join("/usr", "local", "bin"); // "/usr/local/bin"
path.resolve("src", "index.ts"); // 返回绝对路径
path.dirname("/app/src/index.ts"); // "/app/src"
path.basename("/app/src/index.ts"); // "index.ts"
path.extname("report.pdf"); // ".pdf"
path.parse("/app/src/index.ts");
// { root: "/", dir: "/app/src", base: "index.ts", ext: ".ts", name: "index" }
// ESM 模块中获取 __dirname(ESM 没有 __dirname 全局变量)
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// 实际用法:读取同目录下的配置文件
const configPath = path.join(__dirname, "config.json");
5. JSON 文件操作
JSON 是最常用的数据交换格式。TypeScript 可以结合类型系统来确保类型安全。
import { readFile, writeFile } from "fs/promises";
// 定义接口
interface AppConfig {
port: number;
host: string;
debug: boolean;
}
// 读取并解析 JSON
async function loadConfig(filePath: string): Promise<AppConfig> {
const raw = await readFile(filePath, "utf-8");
return JSON.parse(raw) as AppConfig;
}
// 序列化并写入 JSON
async function saveConfig(filePath: string, config: AppConfig): Promise<void> {
const json = JSON.stringify(config, null, 2);
await writeFile(filePath, json, "utf-8");
}
使用 zod 进行运行时验证,确保外部数据真的符合预期类型:
import { z } from "zod";
const ConfigSchema = z.object({
port: z.number().int().min(1).max(65535),
host: z.string(),
debug: z.boolean(),
});
type Config = z.infer<typeof ConfigSchema>;
const raw = JSON.parse(await readFile("config.json", "utf-8"));
const config = ConfigSchema.parse(raw); // 验证失败会抛出 ZodError
6. CSV 处理
处理 CSV 文件推荐使用 csv-parse 和 csv-stringify 库。
npm install csv-parse csv-stringify
import { parse } from "csv-parse/sync";
import { readFileSync } from "fs";
interface SalesRecord {
date: string;
product: string;
amount: number;
}
const csvContent = readFileSync("./sales.csv", "utf-8");
const records = parse(csvContent, {
columns: true, // 首行作为列名
skip_empty_lines: true,
cast: (value, context) => {
if (context.column === "amount") return Number(value);
return value;
},
}) as SalesRecord[];
records.forEach((r) => console.log(`${r.date}: ${r.product} - ¥${r.amount}`));
📝 本章要点
优先使用 fs/promises
基于 Promise 的异步 API 是现代标准
大文件用 Stream
避免内存溢出,用 pipeline() 管道连接
路径拼接用 path 模块
跨平台兼容,ESM 用 import.meta.url
JSON 解析加类型守卫
用 zod 做运行时验证更安全