01 路径、文件和目录的基础模型

1. 路径只是定位信息

下面都只是字符串,并不证明文件存在:

const relativePath = "docs/index.md";
const absolutePath = "D:\\loop\\nloop\\docs\\index.md";

文件系统操作发生时,操作系统才会检查存在性、权限和文件类型。

2. 相对路径相对于谁

Node.js 的相对文件路径通常相对于:

process.cwd()

它是启动进程时的工作目录,不是当前 .ts 文件目录。

console.log({
  cwd: process.cwd(),
  sourceDirectory: __dirname,
  sourceFile: __filename,
});

假设在项目根目录运行:

ts-node tools/read-config.ts

那么 readFile("config.yaml") 从项目根查找。若先进入 tools 再运行,查找位置会变化。

3. 文件、目录与扩展名

扩展名只是命名约定:

report.md.exe
archive.zip
archive.zip.txt

.zip 不能证明内容真是 ZIP。业务通常需要同时检查:大小、魔数、能否解析、Entry 结构和解压限制。

4. URL 路径不是文件路径

/assets/logo.png       URL pathname
D:\site\assets\logo.png Windows 文件路径
/var/www/assets/logo.png Linux 文件路径

不要对 URL 使用 path.join() 后直接返回给浏览器;也不要把 URL pathname 直接当成本地路径。

5. 绝对路径用于边界,数据库保存逻辑键

运行时操作文件宜解析为绝对路径:

import path from "node:path";

const storageRoot = path.resolve("storage");

数据库更适合保存:

projects/42/result/abc.zip

而不是保存某台服务器专属的:

D:\data\nloop\projects\42\result\abc.zip

部署到 Ubuntu 或对象存储时,逻辑存储键更容易迁移。

6. 实操:观察 cwd

import { readFile } from "node:fs/promises";
import path from "node:path";

async function main(): Promise<void> {
  const inputPath = path.resolve("package.json");
  console.log({ cwd: process.cwd(), inputPath });

  const content = await readFile(inputPath, "utf8");
  console.log(content.slice(0, 80));
}

main().catch((error: unknown) => {
  console.error(error);
  process.exitCode = 1;
});

分别从项目根目录和其他目录启动,记录差异。

常见错误

练习题

  1. process.cwd()__dirname 分别由什么决定?
  2. 为什么数据库保存绝对磁盘路径会增加部署迁移成本?
  3. 设计 originalNamestorageKeyabsolutePath 三者的职责。
  4. 从三个不同启动目录运行同一个读取脚本并记录结果。