Skip to content

内置模块与异常处理语法(Day7 1/3) ​

本页对应 Day07_Node,共 7 段视频,约 1 小时 29 分。

学习目标 ​

  • 能够解释 Node.js 中相对路径的参照对象,并使用 __dirname 配合 path.resolve 或 path.join 构造稳定的绝对路径,避免因命令行运行目录不同导致文件读写失败。
  • 能够使用 fs.mkdir / fs.mkdirSync 创建目录,并通过 recursive: true 递归创建多级目录,同时处理目录已存在或创建失败的情况。
  • 能够使用 fs.rmdir / fs.rmdirSync 删除空目录,使用递归选项删除非空目录,并使用 fs.readdir / fs.readdirSync 读取目录内容、遍历条目及拼接子项绝对路径。
  • 能够使用 fs.access / fs.accessSync 判断文件或目录是否存在,并分别采用回调错误对象或 try...catch 处理判断结果。
  • 能够使用 fs.stat / fs.statSync 获取文件状态,并通过 isFile()、isDirectory() 判断目标类型,从而选择正确的删除或处理方式。
  • 能够使用 fs.createReadStream 监听 data、end、error 事件实现流式读取,并使用 fs.createWriteStream 配合 write()、close() 实现流式写入,说明流式操作适合大文件且能降低内存占用。

知识点 ​

相对路径到底相对谁 ​

为什么需要:读写文件必须给路径;路径写错,程序就找不到文件。怎么想:HTML/CSS 里的相对路径参照“写代码的文件”,但 Node 脚本不一定。转写稿中在 01-内置模块FS 目录运行能读到 ./data/a.txt,cd .. 到上级目录再运行就报“找不到文件或目录”。原因是 Node 的相对路径参照命令行当前工作目录,不是脚本文件所在目录。怎么做:用 __dirname 拿到脚本所在目录,再拼绝对路径:

js
const fs = require('fs');
const path = require('path');
const fileName = path.resolve(__dirname, './data/a.txt');
fs.readFile(fileName, 'utf8', (err, data) => {
  if (err) return console.log(err.message);
  console.log(data);
});

path.join(__dirname, ...) 也能拼;区别是 resolve 会转绝对路径,join 是纯拼接,但 __dirname 本身已是绝对路径,所以本例结果一样。易错点:不要手写字符串拼路径;不要把“文件名”只理解成 basename,编程里常指完整路径;不要以为 resolve('./data/a.txt') 能脱离命令行目录。

创建目录:mkdir 与递归 ​

为什么需要:程序常要自动建日志目录、按日期建目录。怎么想:mkdir 是 make directory;异步回调只收 err。普通 mkdir 要求父目录已存在,所以 d/news/contents 会失败。怎么做:

js
const newDir = path.join(__dirname, 'd/news/contents');
fs.mkdir(newDir, { recursive: true }, (err) => {
  if (err) return console.log('创建失败', err.message);
  console.log('创建目录成功');
});

同步用 fs.mkdirSync(newDir, { recursive: true }),失败要 try/catch。易错点:时间戳做目录名要转字符串;不传 recursive:true 只能建最后一级;重复创建已存在目录可能报 EEXIST。

删除目录与读取目录 ​

为什么需要:维护日志、清理临时目录;读取目录可批量处理文件。怎么想:rmdir 只能删空目录,删非空会报 directory not empty;递归删除要加 { recursive: true },但转写稿明确提醒这很危险,官方也建议先 readdir 再逐个删。怎么做:

js
fs.readdir(path.join(__dirname, 'd'), (err, files) => {
  if (err) return console.log(err.message);
  files.forEach(baseName => {
    console.log(path.resolve(__dirname, 'd', baseName));
  });
});

删除目录:fs.rmdir(dir, { recursive: true }, cb);同步 rmdirSync 配合 try/catch。易错点:删除文件和删除目录方法不同,删文件用 unlink;readdir 得到的是目录项名字数组,不是完整路径;递归删除可能清空重要目录,不能手抖。

判断文件或目录是否存在 ​

为什么需要:后续操作前先判断目标是否存在,避免直接报错。怎么想:exists 已弃用但仍可用;推荐 access。怎么做:

js
fs.access(file, fs.constants.F_OK, (err) => {
  if (err) return console.log('文件不存在');
  console.log('文件存在');
});

同步 accessSync 不靠返回值判断,而要靠 try/catch:不抛错即存在。易错点:异步 access 的回调先入回调队列,同步代码会先执行;不要把 access 当成返回布尔值的函数。

判断是文件还是目录 ​

为什么需要:批量删除时,目录用 rmdir,文件要用 unlink,必须先分辨类型。怎么想:stat 返回状态对象,里面有 isDirectory()、isFile()。怎么做:

js
fs.stat(target, (err, stats) => {
  if (err) return console.log(err.message);
  console.log(stats.isDirectory(), stats.isFile());
});

同步用 statSync,同样要 try/catch。易错点:目标不存在时 stat 会报错;控制台展开对象不等于拿到判断结果,要调用方法。

流式读取文件 ​

为什么需要:readFile 会把大文件一次性读进内存,读视频等大文件占内存且慢;流式读取一次约 64KB。怎么想:创建读取流,监听 data、end、error。怎么做:

js
const rs = fs.createReadStream(fileName);
rs.on('data', chunk => console.log(chunk)); // Buffer
rs.on('end', () => console.log('读取完毕'));
rs.on('error', err => console.log(err.message));

易错点:视频 Buffer 不能随便 toString 成文字;大文件不要用 readFile 硬读;路径仍要用 __dirname 拼绝对路径。

流式写入文件 ​

为什么需要:大量内容写入时,流式写入一点点写,内存占用小。怎么想:createWriteStream 创建写入流,用 write 写字符串或 Buffer,写完 close,监听 close 表示结束。怎么做:

js
const ws = fs.createWriteStream(path.join(__dirname, 'd/content.txt'));
for (let i = 0; i < 100000; i++) {
  ws.write(`${i} ${Date.now()}\n`);
}
ws.close();
ws.on('close', () => console.log('写入完毕'));

易错点:转写稿中循环没执行导致“没写进去”,要先检查 for 是否真的跑了;字符串写入会按 UTF-8 编码;写入流用完应及时关闭,避免占用资源。

内置模块与引入方式 ​

Node 中的模块分为内置模块、第三方模块和自定义模块。为什么需要:模块化能把功能拆开,内置模块由 Node 直接提供,不用安装,但使用前必须先引入。怎么想:内置模块写模块名,如 fs、path;第三方模块需要先安装;自定义模块按路径引入。怎么做:

js
const fs = require('fs');
const path = require('path');

易错点:require('fs') 中模块名固定,变量名可自己起;普通 Node 脚本里不要写 HTML 的 <script>;require('./xxx') 的相对路径是相对当前脚本文件,而 fs 读写文件时的相对路径是相对命令行运行目录,二者不要混。

相对路径的坑与绝对路径 ​

为什么需要:读写文件必须给路径,路径写错就报 no such file or directory。怎么想:转写稿中在 01-内置模块 目录下运行成功,cd .. 到上一级再运行就失败,因为 ./data/a.txt 参照的是命令行所在目录,不是脚本所在目录。怎么做:用 __dirname 拿到脚本所在目录,再用 path.resolve 或 path.join 拼接:

js
const file = path.resolve(__dirname, 'data/a.txt');
fs.readFile(file, 'utf8', (err, data) => {
  if (err) return console.log('读取失败', err.message);
  console.log(data);
});

易错点:只写 path.resolve('data/a.txt') 仍会按命令行目录解析;必须把 __dirname 放在前面。join 是纯拼接,resolve 会转成绝对路径;当前面是 __dirname 时两者结果通常一样。调试时先 console.log(__dirname) 确认基准目录。

fs 创建目录 ​

为什么需要:程序常要自动建日志目录、临时目录。怎么想:创建目录用 mkdir,异步带回调,同步用 mkdirSync;要一次建多层目录,加 { recursive: true }。怎么做:

js
const newDir = path.resolve(__dirname, `${Date.now()}`);
fs.mkdir(newDir, { recursive: true }, err => {
  if (err) return console.log('创建失败', err.message);
  console.log('创建目录成功');
});

同步写法:

js
try {
  fs.mkdirSync(newDir, { recursive: true });
} catch (e) {
  console.log('创建失败', e.message);
}

易错点:目录名必须是字符串,时间戳要放进模板字符串;非递归时父目录必须已存在;目录已存在会报 EEXIST。递归创建多级目录时,recursive: true 不能漏。

fs 删除目录与读取目录 ​

为什么需要:清理旧目录、遍历目录内容。怎么想:rmdir 只能删空目录;非空目录要递归删除,但转写稿也提醒递归删除危险。读取目录用 readdir,返回名称数组。怎么做:

js
fs.rmdir(dir, { recursive: true }, err => {
  if (err) return console.log('删除失败', err.message);
  console.log('删除成功');
});

fs.readdir(dir, (err, files) => {
  if (err) return console.log('读取失败', err.message);
  files.forEach(name => {
    const full = path.resolve(dir, name);
    console.log(full);
  });
});

同步读取:const files = fs.readdirSync(dir);。易错点:rmdir 删非空目录报 ENOTEMPTY;删除文件要用 unlink,不是 rmdir;readdir 只读一层,返回的是名称,拼完整路径要用 path.resolve(dir, name);递归删除别对根目录操作。

判断存在与文件类型 ​

为什么需要:操作前先判断,避免直接报错;删除时还要区分文件还是目录。怎么想:exists 已弃用,转写稿建议用 access 判断是否存在;用 stat 拿状态,再调用 isDirectory()、isFile()。怎么做:

js
fs.access(file, fs.constants.F_OK, err => {
  if (err) return console.log('文件不存在');
  console.log('文件存在');
});

fs.stat(target, (err, stats) => {
  if (err) return console.log('判断失败', err.message);
  console.log(stats.isDirectory(), stats.isFile());
});

易错点:access 同步版 accessSync 出错会抛异常,要 try/catch;stat 文件不存在会报错;同步代码会先于异步回调执行,输出顺序不要误判。

流式读写文件 ​

为什么需要:readFile 会把大文件一次性读进内存,视频等大文件很占内存;流式读写一次处理约 64KB。怎么想:createReadStream 触发 data、end、error;createWriteStream 用 write 写、end 结束。怎么做:

js
const rs = fs.createReadStream(file);
rs.on('data', chunk => {
  console.log(chunk); // Buffer
});
rs.on('end', () => console.log('读取完毕'));
rs.on('error', err => console.log('读取出错', err.message));

const ws = fs.createWriteStream(outFile);
ws.write('hello\n');
ws.end();
ws.on('close', () => console.log('写入结束'));

易错点:data 拿到的是 Buffer,视频不能当字符串看;写流结束要 end(),否则可能占资源;循环写要确认循环真的执行;流式读取不是回调一次返回全部内容。

先看异常处理的共同套路:错误优先 ​

本节虽然叫“异常处理语法”,但在 Node 的内置模块 fs 里,异常并不是只靠 try/catch 处理。转写稿里反复出现两种写法:异步 API 的回调函数第一个参数是 err,同步 API 出错则直接抛异常,需要 try/catch。为什么需要先建立这个套路?因为文件读写、创建目录、删除目录都可能失败:路径不存在、目录非空、参数类型不对、权限不足。如果只看“成功分支”,程序会在错误发生时给出很难定位的结果。怎么想?先判断“有没有错误对象”,再走成功逻辑。怎么做?异步写成 if (err) { ... } else { ... };同步写成 try { ... } catch (err) { ... }。易错点:回调里不要只写 console.log(data),否则 err 存在时 data 可能是 undefined;同步 API 不会把错误塞进返回值,必须捕获。

相对路径的参照点不是脚本文件 ​

转写稿先调试了一个典型问题:在 01-内置模块FS 目录下运行 01-相对路径问题.js,readFile('./data/a.txt') 能成功;切到上一级目录再运行同一个脚本,就报“找不到文件或目录”。为什么?因为 Node 中相对路径的参照点不是“写代码的 JS 文件”,而是命令行当前所在目录。怎么想?只要终端打开位置可能变化,脚本里的相对路径就会变化。怎么做?用 __dirname 拿到脚本所在目录,再拼路径:

js
const fs = require('fs');
const path = require('path');

const filename = path.resolve(__dirname, 'data/a.txt');
fs.readFile(filename, 'utf8', (err, data) => {
  if (err) return console.log('读取失败:', err.message);
  console.log(data);
});

易错点:path.resolve(__dirname, './data/a.txt') 里的相对部分仍然会基于 __dirname 解析;不要自己手拼字符串。path.join 是纯拼接,path.resolve 会转绝对路径;当前面已经是 __dirname 时,两者结果通常一致。

创建目录:异步回调与同步 try/catch ​

创建目录用 fs.mkdir。为什么需要异常处理?如果目录已存在、父目录不存在、参数不是字符串,都会失败。转写稿中先用时间戳当目录名,结果报参数类型错误:path 必须是字符串。怎么想?先保证路径是字符串,再判断是否需要递归创建多级目录。怎么做?异步:

js
const newDir = path.resolve(__dirname, `image-${Date.now()}`);
fs.mkdir(newDir, (err) => {
  if (err) return console.log('创建失败:', err.message);
  console.log('创建成功');
});

多级目录要加 { recursive: true },否则父目录不存在会失败。同步写法:

js
try {
  fs.mkdirSync(newDir, { recursive: true });
  console.log('创建成功');
} catch (err) {
  console.log('创建失败:', err.message);
}

易错点:Date.now() 是数字,拼路径时要用模板字符串或 String();重复创建同名目录会报已存在,除非递归选项下已存在时不再报错。

删除目录:空目录限制与递归风险 ​

删除目录用 fs.rmdir。为什么容易出错?它默认只能删除空目录。转写稿中删除 image 成功,再删就失败;删除非空 d 时报 directory not empty。怎么想?先确认目录是否为空;非空目录要用递归删除,但递归删除很危险。怎么做?

js
fs.rmdir(dir, { recursive: true }, (err) => {
  if (err) return console.log('删除失败:', err.message);
  console.log('删除成功');
});

同步写法同样用 try/catch。易错点:递归删除等于“里面有什么都删”,手误写到根目录后果严重;转写稿也提到官方更建议先读目录、再逐个删除,而不是野蛮递归。

读取目录与判断存在、类型 ​

读取目录用 fs.readdir,回调有两个参数:err 和文件名字符串数组。为什么需要?批量处理目录内容时,要先知道里面有哪些文件或子目录。怎么做?

js
fs.readdir(dir, (err, files) => {
  if (err) return console.log('读取失败:', err.message);
  files.forEach(name => {
    const full = path.resolve(dir, name);
    console.log(full);
  });
});

判断存在可用 fs.access,同步版出错要 try/catch;判断是文件还是目录用 fs.stat:

js
fs.stat(target, (err, stats) => {
  if (err) return console.log('判断失败:', err.message);
  console.log(stats.isDirectory(), stats.isFile());
});

易错点:access 同步版不是返回布尔值,而是“不报错即存在”;stat 同步版也要捕获异常。

流式读写:用 error 事件兜底 ​

大文件不要一次性 readFile,否则会把整个文件读进内存。流式读取用 fs.createReadStream,每次读约 64KB,触发 data 事件;流式写入用 fs.createWriteStream,用 write 写、close 关闭。为什么需要异常处理?流过程中可能触发 error。怎么做?

js
const rs = fs.createReadStream(bigFile);
rs.on('data', chunk => console.log(chunk)); // Buffer
rs.on('error', err => console.log('读取出错:', err.message));
rs.on('end', () => console.log('读取完毕'));

写入:

js
const ws = fs.createWriteStream(outFile);
for (let i = 0; i < 1000; i++) {
  ws.write(`${i} ${Date.now()}\n`);
}
ws.close();
ws.on('close', () => console.log('写入结束'));

易错点:data 拿到的是 Buffer,视频等二进制内容不能随便当字符串看;写入流结束要 close,否则可能占用资源或写入不及时。

课堂画面 ​

课堂画面 1

课堂画面 2

课堂画面 3

课堂画面 4

课堂画面 5

课堂画面 6

代码示例 ​

代码一:相对路径问题与 __dirname 拼接绝对路径

js
// 01-相对路径问题.js
// 运行前:先在本文件同级新建 data/a.txt,随便写点内容
const fs = require('fs');
const path = require('path');

// 错误示范(注释掉):直接写相对路径
// const filePath = './data/a.txt';
// 命令行在哪个目录打开,'./data/a.txt' 就相对哪个目录去找

// 正确做法:__dirname 永远是「脚本文件所在目录」
const filePath = path.resolve(__dirname, './data/a.txt');
console.log('文件地址:', filePath);

fs.readFile(filePath, 'utf8', (err, data) => {
  if (err) {
    console.log('读取失败:', err.message);
    return;
  }
  console.log('读取成功:', data);
});

要点解释与易错点:

  • require('fs') / require('path') 是内置模块,直接写名字即可,不需要 ./。
  • 相对路径在 Node 里参照的是命令行所在的目录,不是写代码的 js 文件所在目录;换一个目录执行 node 01-相对路径问题.js 就会报 no such file or directory。
  • __dirname 与命令行打开位置无关,永远是脚本文件所在目录,所以先用它做基准。
  • path.resolve 会把结果转成绝对路径;path.join 只是纯拼接,只是因为前面是 __dirname(本身已是绝对路径)结果才看起来一样。二者在本例可任选其一,但不要自己用字符串拼路径。
  • 不要给 readFile 传 'utf8' 时得到的是 Buffer,需要 data.toString() 才能看到文字;传了编码则回调第二个参数直接是字符串。
  • err 存在即读取失败,用 err.message 打印原因(一般是英文,如文件/目录找不到)。

代码二:目录的创建、读取与删除

js
// 02-创建目录.js
const fs = require('fs');
const path = require('path');

// 1. 创建单层目录:用时间戳做目录名,每次执行名字都不同
const newDir = path.resolve(__dirname, `./${Date.now()}`);
fs.mkdir(newDir, (err) => {
  if (err) {
    console.log('创建目录失败:', err.message);
  } else {
    console.log('创建目录成功:', newDir);
  }
});

// 2. 递归创建多级目录:第二个参数传 { recursive: true }
const multiDir = path.resolve(__dirname, './d/news/contents');
fs.mkdir(multiDir, { recursive: true }, (err) => {
  if (err) {
    console.log('创建多级目录失败:', err.message);
    return;
  }
  console.log('创建多级目录成功:', multiDir);

  // 3. 读取目录:回调有两个参数 (err, files),files 是数组
  fs.readdir(multiDir, (err, files) => {
    if (err) {
      console.log('读取目录失败:', err.message);
      return;
    }
    console.log('目录内容:', files);
    files.forEach((baseName) => {
      // 用 __dirname/目录地址 + 文件名 拼出每一项的绝对路径
      console.log(path.resolve(multiDir, baseName));
    });
  });
});

// 4. 同步方式创建目录:没有回调,失败会抛错,必须 try...catch
try {
  const syncDir = path.resolve(__dirname, `./${Date.now()}-sync`);
  fs.mkdirSync(syncDir);
  console.log('同步创建成功:', syncDir);
} catch (e) {
  console.log('同步创建失败:', e.message);
}

// 5. 删除非空目录:默认 rmdir 只能删空目录,非空要加 { recursive: true }
fs.rmdir(multiDir, { recursive: true }, (err) => {
  if (err) {
    console.log('删除目录失败:', err.message);
    return;
  }
  console.log('删除目录成功');
});

要点解释与易错点:

  • mkdir 第一个参数必须是字符串路径,直接传 Date.now() 这种数字会报 The "path" argument must be of type string,用模板字符串 `./${Date.now()}` 包一层。
  • 不传 { recursive: true } 时,父目录必须已存在,否则报 no such file or directory;只有加了它才能一次建出 d/news/contents 这种多级目录。
  • readdir 的回调是 两个参数 (err, files),第一个是错误对象,第二个才是读到的内容数组;纯操作类方法(mkdir、rmdir)回调只有 err 一个参数。
  • rmdir 只能删空目录,目录里只要有东西就报 directory not empty;非空需 { recursive: true }(该用法已标弃用,Node 新版本更推荐 fs.rm,官方理由是递归删除太危险)。
  • 同步方法(mkdirSync/rmdirSync/readdirSync)没有回调、靠返回值,出错会直接抛异常,所以一定要包 try...catch,否则整个脚本挂掉。

代码三:判断存在、判断文件/目录、流式读写

js
// 03-判断与流式读写.js
const fs = require('fs');
const path = require('path');

// 保证 ./d 目录存在,便于后面写入
fs.mkdirSync(path.resolve(__dirname, './d'), { recursive: true });

const fileA = path.resolve(__dirname, './a.txt'); // 存在
const fileB = path.resolve(__dirname, './b.txt'); // 不存在

// 1. 异步判断文件/目录是否存在:fs.access + F_OK
fs.access(fileA, fs.constants.F_OK, (err) => {
  if (err) {
    console.log('文件不存在');
    return;
  }
  console.log('文件存在');

  // 2. 判断是文件还是目录:fs.stat 拿 stats 对象
  fs.stat(fileA, (err, stats) => {
    if (err) {
      console.log('判断失败:', err.message);
      return;
    }
    console.log('是否目录:', stats.isDirectory());
    console.log('是否文件:', stats.isFile());
  });
});

// 3. 同步判断存在:accessSync 不报错就代表存在,报错走 catch
try {
  fs.accessSync(fileB, fs.constants.F_OK);
  console.log('b.txt 存在');
} catch (e) {
  console.log('b.txt 不存在');
}

// 4. 流式读取:每读一块触发一次 data 事件,一块最多 64KB
const rs = fs.createReadStream(__filename); // 这里读脚本自身,可换成大视频文件
rs.on('data', (chunk) => {
  console.log('读到一块,长度:', chunk.length); // chunk 是 Buffer
});
rs.on('end', () => {
  console.log('读取完毕');
});
rs.on('error', (err) => {
  console.log('读取出错:', err.message);
});

// 5. 流式写入:write 逐条写,写完 close 关闭流
const ws = fs.createWriteStream(path.resolve(__dirname, './d/content.txt'));
ws.on('close', () => {
  console.log('写入完毕');
});
for (let i = 1; i <= 1000; i++) {
  ws.write(`${i} 实数 ${Math.random()} 时间戳 ${Date.now()} 发行\n`);
}
ws.close();

要点解释与易错点:

  • fs.exists 已弃用,用 fs.access(path, fs.constants.F_OK, cb) 代替;模式常量 F_OK 只判断存在,不关心可读可写。
  • accessSync 不是靠返回值判断,而是「不抛错就是存在」,必须配 try...catch,这点和 statSync 返回对象不同。
  • fs.stat 的回调是 (err, stats),判断文件类型用 stats.isDirectory() / stats.isFile(),两个方法都返回布尔值。
  • 流式读取不会一次性进内存,而是每块触发 data 事件,chunk 是 Buffer;读视频这类文件不要 toString(),只有文本才谈编码。
  • 流式写入的 close 事件在 ws.close() 之后触发;write 循环是同步执行的,循环结束后再 close 才符合预期(漏写循环体就不会真正写进去)。
  • 事件名别写错:读取是 data / end / error,写入关闭是 close。

常见错误 ​

  1. 现象:fs.readFile('./data/a.txt') 在脚本目录能读到,换到项目根目录运行就报“找不到文件或目录”。→ 原因:相对路径参照的是命令行当前工作目录,不是脚本文件所在目录。→ 修法:用 path.resolve(__dirname, 'data/a.txt') 或 path.join(__dirname, 'data/a.txt') 拼绝对路径。

  2. 现象:fs.mkdir(newDirName, cb) 报参数类型错误。→ 原因:目录名用了 Date.now() 数字,未转成字符串。→ 修法:用模板字符串或 String(Date.now()) 转成字符串后再传入。

  3. 现象:fs.mkdir('a/b/c', cb) 创建多级目录失败。→ 原因:父目录不存在,默认只创建最后一级目录。→ 修法:传 { recursive: true },如 fs.mkdir(dir, { recursive: true }, cb)。

  4. 现象:fs.rmdir(dir, cb) 删除非空目录失败,提示 directory not empty。→ 原因:rmdir 默认只能删空目录。→ 修法:确认要递归删除时传 { recursive: true };生产环境先读取目录内容并逐项处理,避免误删。

  5. 现象:循环读取目录后统一用 fs.rmdir 删除,目录删掉了但文件删不掉。→ 原因:文件和目录用了同一个删除方法。→ 修法:先用 fs.stat 判断 isDirectory() / isFile(),目录用 rmdir,文件用 unlink。

  6. 现象:流式读取视频后把 chunk 转字符串,出现乱码或无意义内容。→ 原因:视频是二进制数据,不是 UTF-8 文本。→ 修法:流式读取二进制文件时保留 Buffer,不要按字符串处理;只有文本文件才考虑编码转换。

练习 ​

  1. 在 01-相对路径问题.js 中分别用“直接相对路径”和 path.resolve(__dirname, ...) 读取同一文件,并从两个不同目录运行,记录结果差异。

  2. 写一个脚本:以时间戳为目录名创建目录,再在其下创建多级子目录;要求父目录不存在时也能一次创建成功。

  3. 读取指定目录下的所有条目,遍历判断每个条目是文件还是目录,分别用正确的方法删除,并输出每项处理结果。

  4. 用流式读取读取一个大文件,监听 data、end、error 事件,统计读取到的 chunk 次数和总字节数。

  5. 用流式写入向文件写入 1 万行内容,每行包含序号、时间戳和固定文本;写入结束后调用 close(),再检查文件行数与大小。

原视频对照 ​

#视频时长
1node中的相对路径问题18:36
2内置模块fs-创建目录15:45
3内置模块fs-删除目录和读取目录20:25
4内置模块fs-判断文件或目录是否存在08:19
5内置模块fs-判断是文件还是目录08:16
6内置模块fs-流式读取文件11:00
7内置模块fs-流式写入文件06:46

小结 ​

  1. Node 中凡涉及文件路径,优先用 __dirname 配合 path.resolve / path.join,不要依赖命令行打开位置。

  2. fs 的异步方法回调参数要先判断 err,再处理成功结果;同步方法要用 try...catch 兜底。

  3. 创建、删除目录时,先明确是否需要递归;递归删除属于高风险操作,必须先确认目标路径。

  4. 删除前先判断文件还是目录,文件与目录的删除方法不能混用。

  5. 判断存在性、文件类型时,用 access、stat 等推荐方法,不要继续依赖已弃用写法。

  6. 大文件读写优先考虑流式操作,二进制文件不要按文本字符串处理。

  7. 流式写入结束要主动 close(),避免资源占用和后续操作时序问题。