6、文件系统

FS 简介

Node.js 的文件系统模块(fs 模块)提供了丰富的 API,用于读取、写入、删除文件以及执行其他文件系统操作。

fs 模块既支持同步方法也支持异步方法,使得开发者可以根据具体需求选择合适的方式来处理文件操作。

Node.js 文件系统(fs 模块)模块中的方法均有异步和同步版本,例如读取文件内容的函数有异步的 fs.readFile() 和同步的 fs.readFileSync()

异步的方法函数最后一个参数为回调函数,回调函数的第一个参数包含了错误信息(error)。

建议大家使用异步方法,比起同步,异步方法性能更高,速度更快,而且没有阻塞。

导入

var fs = require("fs")

Demo

var fs = require("fs");

// 异步读取
fs.readFile('input.txt', function (err, data) {
   if (err) {
       return console.error(err);
   }
   console.log("异步读取: " + data.toString());
});

// 同步读取
var data = fs.readFileSync('example.txt');
console.log("同步读取: " + data.toString());

console.log("程序执行完毕。");

文件操作

文件写入

方法 说明
writeFile 异步写入
writeFileSync 同步写入
appendFile 异步追加写入
appendFileSync 同步追加写入
createWriteStream 流式写入

writeFile 异步写入

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

参数说明:

fs.writeFile('example.txt', 'Hello, World!', (err) => {
    if (err) {
        console.error('Error writing file:', err);
        return;
    }
    console.log('File written successfully');
});

writeFileSync 同步写入

fs.writeFileSync(file, data[, options])

参数与 fs.writeFile 大体一致,只是没有 callback 参数

try {
    fs.writeFileSync('example.txt', 'Hello, World!');
    console.log('File written successfully');
} catch (err) {
    console.error('Error writing file:', err);
}

appendFile 和 appendFileSync 追加写入

appendFile 作用是在文件尾部追加内容,appendFile 语法与 writeFile 语法完全相同

异步

fs.appendFile('example.txt', '\nAppending some text', (err) => {
    if (err) {
        console.error('Error appending to file:', err);
        return;
    }
    console.log('Text appended successfully');
});

同步

try {
    fs.appendFileSync('example.txt', '\nAppending some text');
    console.log('Text appended successfully');
} catch (err) {
    console.error('Error appending to file:', err);
}

createWriteStream 流式写入

fs.createWriteStream(path[, options])

参数说明:

const fs = require('fs');

const writableStream = fs.createWriteStream('output.txt');

writableStream.write('Hello, ');
writableStream.write('World!\n');

writableStream.end(); // 完成写入操作。此时可以关闭流或进行其他操作。

writableStream.on('finish', () => {
    console.log('All writes are now complete.');
});

文件读取

方法 说明
readFile 异步读取
readFileSync 同步读取
createReadStream 流式读取

readFile 异步读取

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

参数说明:

//导入 fs 模块
const fs = require('fs');
fs.readFile('./input.txt', (err, data) => {
  if(err) throw err;
  console.log(data);
});
fs.readFile('./input.txt', 'utf-8',(err, data) => {
  if(err) throw err;
  console.log(data);
});

readFileSync 同步读取

fs.readFileSync(path[, options])

参数说明:

let data = fs.readFileSync('./input.txt');
let data2 = fs.readFileSync('./input.txt', 'utf-8');

createReadStream 流式读取

fs.createReadStream(path[, options])

参数说明:

const fs = require('fs');

const readableStream = fs.createReadStream('large_file.txt', 'utf8');

readableStream.on('data', (chunk) => {
    console.log('Received chunk:', chunk);
});

readableStream.on('end', () => {
    console.log('No more data.');
});

删除文件

异步

fs.unlink('example.txt', (err) => {
    if (err) {
        console.error('Error deleting file:', err);
        return;
    }
    console.log('File deleted successfully');
});

同步

try {
    fs.unlinkSync('example.txt');
    console.log('File deleted successfully');
} catch (err) {
    console.error('Error deleting file:', err);
}

目录操作

借助 Node.js 的能力,我们可以对文件夹进行创建 、 读取 、 删除 等操作

方法 说明
mkdir / mkdirSync 创建文件夹
readdir / readdirSync 读取文件夹
rmdir / rmdirSync 删除文件夹

创建目录

异步

fs.mkdir('new_directory', (err) => {
    if (err) {
        console.error('Error creating directory:', err);
        return;
    }
    console.log('Directory created successfully');
});

同步

try {
    fs.mkdirSync('new_directory');
    console.log('Directory created successfully');
} catch (err) {
    console.error('Error creating directory:', err);
}

读取目录内容

异步

fs.readdir('new_directory', (err, files) => {
    if (err) {
        console.error('Error reading directory:', err);
        return;
    }
    console.log('Directory contents:', files);
});

同步

try {
    const files = fs.readdirSync('new_directory');
    console.log('Directory contents:', files);
} catch (err) {
    console.error('Error reading directory:', err);
}

删除目录

//异步删除文件夹
fs.rmdir('new_directory', err => {
  if(err) throw err;
  console.log('删除成功');
});

//异步递归删除文件夹
fs.rmdir('new_directory', {recursive: true}, err => {
  if(err) {
  	console.log(err);
	}
	console.log('递归删除')
});

//同步递归删除文件夹
fs.rmdirSync('new_directory', {recursive: true})

通用方法

检查文件或目录

异步

fs.access('example.txt', fs.constants.F_OK, (err) => {
    if (err) {
        console.log('File does not exist');
    } else {
        console.log('File exists');
    }
});

同步

try {
    fs.accessSync('example.txt', fs.constants.F_OK);
    console.log('File exists');
} catch (err) {
    console.log('File does not exist');
}

查看资源信息(状态)

方法 说明
stat 异步查看资源信息
statSync 同步查看资源信息

异步

fs.stat('example.txt', (err, stats) => {
    if (err) {
        console.error('Error getting stats:', err);
        return;
    }
  	// 查看资源是否为文件
    console.log('Is file?', stats.isFile());
    // 查看资源是否为目录
    console.log('Is directory?', stats.isDirectory());
    // 资源的大小
    console.log('Size:', stats.size);
});

同步

try {
    const stats = fs.statSync('example.txt');
    console.log('Is file?', stats.isFile());
    console.log('Is directory?', stats.isDirectory());
    console.log('Size:', stats.size);
} catch (err) {
    console.error('Error getting stats:', err);
}

文件移动与重命名

在 Node.js 中,我们可以使用 renamerenameSync 来移动或重命名文件或文件夹

fs.rename(oldPath, newPath, callback)

fs.renameSync(oldPath, newPath)

参数说明:

异步

fs.rename('./file.txt', './path/new_file.txt', (err) =>{
  if(err) throw err;
  console.log('移动完成')
});

同步

fs.renameSync('./file.txt', './path/new_file.txt');

fs.open打开文件

以下为在异步模式下打开文件的语法格式:fs.open(path, flags[, mode], callback)

参数使用说明如下:

var fs = require('fs')

fs.open('package.json','r',(err,fd) => {
    if (err) {
        console.error(err)
    }
})

flags 参数可以是以下值:

Flag 描述
r 以只读模式打开文件。文件必须存在。如果文件不存在,会抛出异常
r+ 以读写模式打开文件。文件必须存在。
rs 以同步方式只读打开文件。阻塞操作,但在某些操作系统上可能会提供更好的稳定性。
rs+ 以同步方式读写打开文件。阻塞操作,但在某些操作系统上可能会提供更好的稳定性。
w 以只写模式打开文件。如果文件不存在则创建文件,如果文件存在则截断文件。
wx 类似于 'w',但如果路径存在,则失败。
w+ 以读写模式打开文件。如果文件不存在则创建文件,如果文件存在则截断文件。
wx+ 类似于 'w+',但如果路径存在,则失败。
a 以追加模式打开文件。如果文件不存在则创建文件。
ax 类似于 'a',但如果路径存在,则失败。
a+ 以读取和追加模式打开文件。如果文件不存在则创建文件。
ax+ 类似于 'a+',但如果路径存在,则失败。

读取

参数使用说明如下:

var fs = require("fs");
var buf = new Buffer.alloc(1024);

console.log("准备打开已存在的文件!");
fs.open('input.txt', 'r+', function(err, fd) {
   if (err) {
       return console.error(err);
   }
   console.log("文件打开成功!");
   console.log("准备读取文件:");
   fs.read(fd, buf, 0, buf.length, 0, function(err, bytes){
      if (err){
         console.log(err);
      }
      console.log(bytes + "  字节被读取");
      
      // 仅输出读取的字节
      if(bytes > 0){
         console.log(buf.slice(0, bytes).toString());
      }
   });
});

写入

fs.write(fd, buffer[, options], callback)

var fs = require('fs')

fs.open('input.txt','w',(err,fd) => {
    if (err) {
        console.error(err)
    }
    fs.write(fd,Buffer.from('hello world'),(err,written) => {
        console.log(written + "  字节被写入")
        fs.close(fd) 
    })
})

注意fs.close() 用于关闭由 fs.open 打开的文件描述符,如果文件描述符未正确关闭,会导致资源泄漏,最终耗尽可用的文件描述符,使程序无法打开新的文件或网络连接。另外我们一直说Buffer叫缓冲区,它并不是真正的磁盘,我们只有在关闭文件转换才会写入,这样能够避免文件丢失。

监视文件

对文件进行监视,并且在监视到文件被修改时执行处理

fs.watchFile(filename, [options], listener)

fs.watchFile(__dirname + '/test.txt', {interval: 20}, function (curr, prev) {
  if(Date.parse(prev.ctime) == 0) {
    console.log('文件被创建!');
  } else if(Date.parse(curr.ctime) == 0) {
    console.log('文件被删除!')
  } else if(Date.parse(curr.mtime) != Date.parse(prev.mtime)) {
    console.log('文件有修改');
  }
});

fs.watchFile(__dirname + '/test.txt', function (curr, prev) {
  console.log('这是第二个watch,监视到文件有修改');
});

取消监视器

fs.unwatchFile(filename, [listener])

var listener = function (curr, prev) {
  console.log('我是监视函数')
}
fs.unwatchFile(__dirname + '/test.txt', listener);

监视目录或文件

fs.watch(filename, [options], [listener]);

var fsWatcher = fs.watch(__dirname + '/test', function (event, filename) {
  //console.log(event)
});


fsWatcher.on('change', function (event, filename) {
  console.log(filename + ' 发生变化')
});


//30秒后关闭监视
setTimeout(function () {
  console.log('关闭')
  fsWatcher.close(function (err) {
    if(err) {
      console.error(err)
    }
    console.log('关闭watch')
  });
}, 30000);