Node专栏-fs(文件系统)

489 阅读6分钟

前言

最近在做一个多平台统一部署打包流程优化的专项。 其中插件中的一个小功能就是:将build的产物dist目录下的文件拷贝到.net项目静态资源目录下。于是就有了这个Node专栏。用到哪个模块哪个方法就整理记录一下~

fs(文件系统)

fs 模块可用于与文件系统进行交互(以类似于标准 POSIX 函数的方式)。

要使用此模块:

const fs = require("fs");

所有多文件系统操作都具有同步的、回调的、以及基于 promise的形势。

同步的案例

同步的形式会阻塞 Node.js 事件循环何进一步的JavaScript执行,直到操作完成。异常会被立即抛出,可以使用 try...catch处理,也可以冒泡。

const fs = require("fs");

try{
    fs.unlinkSync('文件');
    console.log('已成功删除文件呢')
}catch(err){
    //处理错误
}

回调的案例

异步的形式总是把完成回调作为其最后一个参数。传给完成回调的参数取决于具体方法,但第一个参数总是预留给异常。如果操作被成功完成,则第一个参数会为null或 undefined。

const fs = require('fs');

fs.unlink('文件',(err)=>{
    if(err) throw err;
    console.log('已成功删除文件呢')
})

Promise的示例

基于promise的操作会返回Promise(当异步操作完成时被解决)。

const fs = require('fs');

(async function(path){
    try{
        await fs.unlink(path);
        console.log(`已成功删除文件${path}`);
    }catch(error){
        console.error('出错:',error.message);
    }
})('文件');

回调与基于promise的操作的顺序

当使用异步的方法时,无法保证顺序。 因此,以下的操作容易出错,因为 fs.stat() 操作可能在 fs.rename() 操作之前完成:

fs.rename('旧文件''新文件',(err)=>{
  if (err) throw err;
  console.log('重命名完成');
});

fs.stat('新文件', (err, stats) => {
  if (err) throw err;
  console.log(`文件属性: ${JSON.stringify(stats)}`);
});

若要正确地排序这些操作,则移动 fs.stat() 调用到 fs.rename() 操作的回调中:

fs.rename('旧文件', '新文件', (err) => {
  if (err) throw err;
  fs.stat('新文件', (err, stats) => {
    if (err) throw err;
    console.log(`文件属性: ${JSON.stringify(stats)}`);
  });
});

或者,使用基于promise的API:

const fs = requir('fs/promises');

(async function(from,to){
    try{
        await fs.rename(from,to);
        const stats = await fs.stat(to);
    }catch(error){
       console.error('出错:', error.message);
    }
})('旧文件','新文件')

fs.writeFile(file,data[,options],callback)

  • file <string> | <Buffer> | <URL> | <integer> 文件名或文件描述符。
  • data <string> | <Buffer> | <TypedArray> | <DataView>
  • options <Object> | <string>
    • encoding <string> | <null> 默认值: 'utf8'。
    • mode <integer> 默认值: 0o666。
    • flag <string> 参见文件系统 flag 的支持。 默认值: 'w'。
  • callback <Function>
    • err <Error>

file是文件名时,则异步地写入数据到文件(如果文件已存在,则覆盖文件)。data可以是字符串或buffer

file是文件描述符时,则其行为类似于直接调用fs.write()(建议使用)。

如果data是 buffer,则encoding选项则会被忽略。 如果data是普通对象,则它必须具有自身的toString函数属性。

const data = new Uint8Array(Buffer.from('Node.js 中文网'));
fs.writeFile('文件.txt', data, (err) => {
  if (err) throw err;
  console.log('文件已被保存');
});

如果 options 是字符串,则它指定字符编码:

fs.writeFile('文件.txt', 'Node.js 中文网', 'utf8', callback);

不等待回调就对同一个文件多次使用 fs.writeFile() 是不安全的。 对于这种情况,建议使用 fs.createWriteStream()。

fs.readdirSync(path[,options],callback)

  • path <string>|<Buffer>|<URL>
  • options <Object>|<string>
    • encoding <string> 默认'utf8'。
    • withFileTypes <boolean> 默认值 false

可选的 options 参数可以是字符串(指定字符编码)、或具有 encoding 属性(指定用于返回的文件名的字符编码)的对象。 如果 encoding 被设置为 'buffer',则返回的文件名会作为 Buffer 对象传入。

如果 options.withFileTypes 被设置为 true,则结果会包含 fs.Dirent 对象。

fs.lstatSync(path[, options])

  • path <string> | <Buffer> | <URL>
  • options <Object>
  • bigint <boolean> 返回的 fs.Stats 对象中的数值是否应为 bigint 型。默认值: false。 返回: <fs.Stats> 同步的 lstat(2)。

fs.Stats类

fs.Stats 对象提供了关于文件的信息。

fs.stat()fs.lstat()fs.fstat()、以及它们的同步方法返回的对象都是此类型。 如果传给这些方法的 options 中的 bigint 为 true,则数值会是 bigint 型而不是 number 型,并且该对象还会包含额外的纳秒级精度的属性(以 Ns 为后缀)。

Stats {
  dev: 2114,
  ino: 48064969,
  mode: 33188,
  nlink: 1,
  uid: 85,
  gid: 100,
  rdev: 0,
  size: 527,
  blksize: 4096,
  blocks: 8,
  atimeMs: 1318289051000.1,
  mtimeMs: 1318289051000.1,
  ctimeMs: 1318289051000.1,
  birthtimeMs: 1318289051000.1,
  atime: Mon, 10 Oct 2011 23:24:11 GMT,
  mtime: Mon, 10 Oct 2011 23:24:11 GMT,
  ctime: Mon, 10 Oct 2011 23:24:11 GMT,
  birthtime: Mon, 10 Oct 2011 23:24:11 GMT }

bigint版本

BigIntStats {
  dev: 2114n,
  ino: 48064969n,
  mode: 33188n,
  nlink: 1n,
  uid: 85n,
  gid: 100n,
  rdev: 0n,
  size: 527n,
  blksize: 4096n,
  blocks: 8n,
  atimeMs: 1318289051000n,
  mtimeMs: 1318289051000n,
  ctimeMs: 1318289051000n,
  birthtimeMs: 1318289051000n,
  atimeNs: 1318289051000000000n,
  mtimeNs: 1318289051000000000n,
  ctimeNs: 1318289051000000000n,
  birthtimeNs: 1318289051000000000n,
  atime: Mon, 10 Oct 2011 23:24:11 GMT,
  mtime: Mon, 10 Oct 2011 23:24:11 GMT,
  ctime: Mon, 10 Oct 2011 23:24:11 GMT,
  birthtime: Mon, 10 Oct 2011 23:24:11 GMT }

stats.isDirectory()

  • 返回 <boolean> 如果 fs.Stats对象描述文件系统目录,则返回 true

如果 fs.Stats 对象来自 fs.lstat(),则此方法会始终返回 false。 这是因为 fs.lstat() 返回关于符号链接本身(而不是其解析的路径)的信息。

fs.existsSync(path)

  • path <string> | <Buffer> | <URL>
  • 返回: <boolean>

如果路径存在,则返回 true,否则返回 false。

if (fs.existsSync('文件')) {
  console.log('该路径已存在');
}

fs.mkdirSync(path[, options])

  • path : <string> | <Buffer> | <URL>isDirectory
  • options: <Object> | <integer>
    • recursive <boolean> 默认值: false
    • mode <string> | <integer> 在 Windows 上不支持。默认值: 0o777。
  • 返回: <string> | <undefined>

同步地创建目录。 返回 undefined,或创建的第一个目录的路径(如果 recursive 为 true)。 这是 fs.mkdir() 的同步版本。

fs.copyFileSync(src,dest[,mode])

  • src <string> | <Buffer> | <URL> 要拷贝的源文件名。
  • dest <string> | <Buffer> | <URL> 拷贝操作的目标文件名。
  • mode <integer> 用于拷贝操作的修饰符。默认值: 0。

同步地将 src 拷贝到 dest。 默认情况下,如果 dest 已经存在,则覆盖它。 返回 undefined。 Node.js 不保证拷贝操作的原子性。 如果在打开目标文件用于写入后发生错误,则 Node.js 将尝试删除目标文件。

mode 是一个可选的整数,指定拷贝操作的行为。 可以创建由两个或更多个值按位或组成的掩码(比如 fs.constants.COPYFILE_EXCL | fs.constants.COPYFILE_FICLONE)。

  • fs.constants.COPYFILE_EXCL - 如果 dest 已存在,则拷贝操作将失败。
  • fs.constants.COPYFILE_FICLONE - 拷贝操作将尝试创建写时拷贝(copy-on-write)链接。如果平台不支持写时拷贝,则使用后备的拷贝机制。
  • fs.constants.COPYFILE_FICLONE_FORCE - 拷贝操作将尝试创建写时拷贝链接。如果平台不支持写时拷贝,则拷贝操作将失败。
const fs = require('fs');
const { COPYFILE_EXCL } = fs.constants;

// 默认情况下将创建或覆盖目标文件。
fs.copyFileSync('源文件.txt', '目标文件.txt');
console.log('源文件已拷贝到目标文件');

// 通过使用 COPYFILE_EXCL,如果目标文件存在,则操作将失败。
fs.copyFileSync('源文件.txt', '目标文件.txt', COPYFILE_EXCL);