02 node:path 核心 API
1. join() 与 resolve()
join() 拼接并规范化片段:
path.join("storage", "jobs", "42", "result.zip");
它不保证得到绝对路径。resolve() 从右向左解析,直到形成绝对路径:
path.resolve("storage", "jobs", "42");
若后面的片段本身是绝对路径,前面的部分会被丢弃:
path.resolve("D:\\safe", "C:\\outside");
因此 resolve(root, userInput) 不是安全校验,只是路径计算。
2. 拆解路径
const parsed = path.parse("docs/guide/start.md");
包含:
dir docs/guide
base start.md
name start
ext .md
生成对应 HTML:
const htmlPath = path.join(
parsed.dir,
`${parsed.name}.html`,
);
这能保留目录层级,不能只调用:
path.basename(inputPath)
否则不同目录下的 index.md 会互相覆盖。
3. relative() 用于比较边界
const relative = path.relative(root, target);
若目标在根目录外,结果通常以 .. 开头,或者在 Windows 上成为绝对路径。
function isInsideRoot(root: string, target: string): boolean {
const relative = path.relative(root, target);
return (
relative !== "" &&
!relative.startsWith(`..${path.sep}`) &&
relative !== ".." &&
!path.isAbsolute(relative)
);
}
是否允许 target === root 应由调用者明确决定,删除操作通常不应允许。
4. 高频 API
| API | 作用 | 常见用途 |
|---|---|---|
dirname() |
父目录 | 创建输出父目录 |
basename() |
最后一段 | 获取展示文件名 |
extname() |
扩展名 | 初步分类,不做安全证明 |
normalize() |
规范化分隔和点段 | 观察路径,不等于安全 |
isAbsolute() |
判断绝对路径 | 拒绝归档绝对路径 |
parse() |
拆解路径 | 替换扩展名 |
format() |
由组件构造路径 | 与 parse() 配合 |
5. 实操:保持相对层级
function createHtmlOutputPath(
sourceRoot: string,
outputRoot: string,
markdownPath: string,
): string {
const relative = path.relative(sourceRoot, markdownPath);
const parsed = path.parse(relative);
return path.join(
outputRoot,
parsed.dir,
`${parsed.name}.html`,
);
}
输入:
source/docs/start.md
输出:
output/docs/start.html
常见错误
- 用字符串
root + "/" + fileName拼路径。 - 认为
normalize()会阻止../。 - 使用
startsWith(root)判断目录边界;C:\data2也可能匹配C:\data。 - 使用
basename()抹平 ZIP 层级。 - 忘记
extname("archive.tar.gz")只返回.gz。
练习题
- 分别输出五组参数经过
join()和resolve()的结果。 - 实现
.md到.html的路径转换,保留目录结构。 - 为
isInsideRoot()编写 Windows 和 POSIX 测试样例。 - 解释为什么
resolve(root, input)之后仍要做边界检查。