Node.js
操作文件
Node.js
文件统计
每个文件都有一组细节,我们可以使用Node.js
检查。特别是使用 fs
模块提供的 stat()
方法。
你调用它传递一个文件路径,一旦Node.js
得到文件的详细信息,它就会调用你传递的回调函数,有两个参数:一条错误消息,以及文件统计信息:
const fs = require('fs');
fs.stat('/Users/joe/test.txt', (err, stats) => {
if (err) {
console.error(err);
}
// we have access to the file stats in `stats`
});
Node.js
还提供了一个sync
方法,该方法会阻塞线程,直到文件状态准备就绪:
const fs = require('fs');
try {
const stats = fs.statSync('/Users/joe/test.txt');
} catch (err) {
console.error(err);
}
文件信息包含在stats
变量中。使用统计数据我们可以提取哪些信息?
如果文件是目录或文件,则使用 stats.isFile()
和 stats.isDirectory()
如果文件是使用 stats.isSymbolicLink()
的符号链接
使用 stats.size
显示文件大小(以字节为单位)。
还有其他的高级方法,但是你在日常编程中要用到的大部分是这个。
const fs = require('fs');
fs.stat('/Users/joe/test.txt', (err, stats) => {
if (err) {
console.error(err);
return;
}
stats.isFile(); // true
stats.isDirectory(); // false
stats.isSymbolicLink(); // false
stats.size; // 1024000 //= 1MB
});
如果你愿意,你也可以使用 fs/promises
模块提供的基于promise
的 fsPromises.stat()
方法:
const fs = require('fs/promises');
async function example() {
try {
const stats = await fs.stat('/Users/joe/test.txt');
stats.isFile(); // true
stats.isDirectory(); // false
stats.isSymbolicLink(); // false
stats.size; // 1024000 //= 1MB
} catch (err) {
console.log(err);
}
}
example();
您可以在官方文档中阅读更多关于 fs
模块的信息。
Node.js
文件路径
系统中的每个文件都有路径。在Linux和macOS上,路径可能如下所示: /users/joe/file.txt
而Windows计算机则不同,其结构如下: C:\users\joe\file.txt
在应用程序中使用路径时需要注意,因为必须考虑到这种差异。
您可以使用 const path = require('path');
将此模块包含在文件中,然后开始使用它的方法。
从路径中获取信息
给定一个路径,您可以使用以下方法从中提取信息:
dirname
:获取文件的父文件夹
basename
:获取文件名部分
extname
:获取文件扩展名
const notes = '/users/joe/notes.txt';
path.dirname(notes); // /users/joe
path.basename(notes); // notes.txt
path.extname(notes); // .txt
您可以通过为 basename
指定第二个参数来获取不带扩展名的文件名:
path.basename(notes, path.extname(notes)); // notes
使用路径
您可以使用 path.join()
连接路径的两个或多个部分:
const name = 'joe';
path.join('/', 'users', name, 'notes.txt'); // '/users/joe/notes.txt'
您可以使用 path.resolve()
获取相对路径的绝对路径计算:
path.resolve('joe.txt'); // '/Users/joe/joe.txt' if run from my home folder
在这种情况下,Node.js
将简单地将 /joe.txt
追加到当前工作目录。如果指定第二个参数文件夹, resolve
将使用第一个作为第二个的基础:
path.resolve('tmp', 'joe.txt'); // '/Users/joe/tmp/joe.txt' if run from my home folder
如果第一个参数以斜杠开头,这意味着它是一个绝对路径:
path.resolve('/etc', 'joe.txt'); // '/etc/joe.txt'
path.normalize()
是另一个有用的函数,当它包含相对说明符(如 .
或 ..
)或双斜杠时,它将尝试计算实际路径:
path.normalize('/users/joe/..//test.txt'); // '/users/test.txt'
resolve和normalize都不会检查路径是否存在。他们只是根据得到的信息计算出一条路径。
Node.js
中使用文件描述符
在能够与文件系统中的文件进行交互之前,必须获得文件描述符。
文件描述符是对打开的文件的引用,使用 fs
模块提供的 open()
方法打开文件时返回的数字(fd
)。此编号( fd
)唯一标识操作系统中打开的文件:
const fs = require('fs');
fs.open('/Users/joe/test.txt', 'r', (err, fd) => {
// fd is our file descriptor
});
请注意,我们使用 r
作为 fs.open()
调用的第二个参数。
该标志意味着我们打开文件进行阅读。
您通常使用的其他标志包括:
标志 | 描述 | 如果文件不存在则创建文件 |
---|---|---|
r+ |
此标志打开文件读取 和写入 | ❌ |
w+ |
此标志打开文件读取 和写入 它还将流定位在开始 文件的 | ✅ |
a |
此标志打开文件写入 它还将流定位在结尾 文件的 | ✅ |
a+ |
此标志打开文件读取 和写入 它还将流定位在结尾 文件的 | ✅ |
您也可以使用 fs.openSync
方法打开文件,该方法返回文件描述符,而不是在回调中提供它:
const fs = require('fs');
try {
const fd = fs.openSync('/Users/joe/test.txt', 'r');
} catch (err) {
console.error(err);
}
一旦你得到了文件描述符,无论你选择什么方式,你都可以执行所有需要它的操作,比如调用 fs.close()
和许多其他与文件系统交互的操作。
您也可以使用 fs/promises
模块提供的基于promise
的 fsPromises.open
方法打开文件。
fs/promises
模块仅从Node.js v14
开始可用。在v14
之前,v10
之后,您可以使用 require('fs').promises
。在v10
之前,在v8
之后,你可以使用 util.promisify
将 fs
方法转换为基于promise
的方法。
const fs = require('fs/promises');
// Or const fs = require('fs').promises before v14.
async function example() {
let filehandle;
try {
filehandle = await fs.open('/Users/joe/test.txt', 'r');
console.log(filehandle.fd);
console.log(await filehandle.readFile({ encoding: 'utf8' }));
} finally {
if (filehandle) await filehandle.close();
}
}
example();
以下是 util.promisify
的示例:
const fs = require('fs');
const util = require('util');
async function example() {
const open = util.promisify(fs.open);
const fd = await open('/Users/joe/test.txt', 'r');
}
example();
要查看更多关于 fs/promises
模块的详细信息,请查看fs/promises API
。
Node.js
读取文件
在Node.js
中读取文件的最简单方法是使用 fs.readFile()
方法,向其传递文件路径、编码和回调函数,该函数将使用文件数据(和错误)调用:
const fs = require('fs');
fs.readFile('/Users/joe/test.txt', 'utf8', (err, data) => {
if (err) {
console.error(err);
return;
}
console.log(data);
});
或者,您可以使用同步版本 fs.readFileSync()
:
const fs = require('fs');
try {
const data = fs.readFileSync('/Users/joe/test.txt', 'utf8');
console.log(data);
} catch (err) {
console.error(err);
}
您也可以使用 fs/promises
模块提供的基于promise的 fsPromises.readFile()
方法:
const fs = require('fs/promises');
async function example() {
try {
const data = await fs.readFile('/Users/joe/test.txt', { encoding: 'utf8' });
console.log(data);
} catch (err) {
console.log(err);
}
}
example();
fs.readFile()
、 fs.readFileSync()
和 fsPromises.readFile()
在返回数据之前读取内存中文件的全部内容。
这意味着大文件将对内存消耗和程序执行速度产生重大影响。
在这种情况下,更好的选择是使用流读取文件内容。
Node.js
写入文件
写入文件
在Node.js
中写入文件的最简单方法是使用 fs.writeFile()
API。
const fs = require('fs');
const content = 'Some content!';
fs.writeFile('/Users/joe/test.txt', content, err => {
if (err) {
console.error(err);
}
// file written successfully
});
同步写入文件
或者,您可以使用同步版本 fs.writeFileSync()
:
const fs = require('fs');
const content = 'Some content!';
try {
fs.writeFileSync('/Users/joe/test.txt', content);
// file written successfully
} catch (err) {
console.error(err);
}
您也可以使用 fs/promises
模块提供的基于promise的 fsPromises.writeFile()
方法:
const fs = require('fs/promises');
async function example() {
try {
const content = 'Some content!';
await fs.writeFile('/Users/joe/test.txt', content);
} catch (err) {
console.log(err);
}
}
example();
默认情况下,如果文件的内容已经存在,此API将替换该文件的内容。
可以通过指定标志来修改默认值:
fs.writeFile('/Users/joe/test.txt', content, { flag: 'a+' }, err => {});
您可能会使用的标志有
标志 | 描述 | 如果文件不存在则创建文件 |
---|---|---|
r+ |
此标志打开 reading 和 writing 的文件 | ❌ |
w+ |
此标志为 reading 和 writing 打开文件,并将流定位在文件的 beginning | ✅ |
a |
此标志为 writing 打开文件,并将流定位在文件的 end | ✅ |
a+ |
此标志为 reading 和 writing 打开文件,并将流定位在文件的 end | ✅ |
您可以在fs
文档中找到有关标志的更多信息。
向文件追加内容
当您不想用新内容覆盖文件,而是想向文件中添加内容时,向文件追加内容非常方便。
例子
将内容附加到文件末尾的一个方便方法是 fs.appendFile()
(及其对应的 fs.appendFileSync()
):
const fs = require('fs');
const content = 'Some content!';
fs.appendFile('file.log', content, err => {
if (err) {
console.error(err);
}
// done!
});
Promises例子
下面是一个 fsPromises.appendFile()
示例:
const fs = require('fs/promises');
async function example() {
try {
const content = 'Some content!';
await fs.appendFile('/Users/joe/test.txt', content);
} catch (err) {
console.log(err);
}
}
example();
Node.js
中使用文件夹
Node.js
fs
核心模块提供了许多方便的方法来处理文件夹。
检查文件夹是否存在
使用 fs.access()
(及其基于promise-based的 fsPromises.access()
对应项)检查文件夹是否存在,Node.js是否可以使用其权限访问它。
创建新文件夹
使用 fs.mkdir()
或 fs.mkdirSync()
或 fsPromises.mkdir()
创建新文件夹。
const fs = require('fs');
const folderName = '/Users/joe/test';
try {
if (!fs.existsSync(folderName)) {
fs.mkdirSync(folderName);
}
} catch (err) {
console.error(err);
}
读取目录的内容
使用 fs.readdir()
或 fs.readdirSync()
或 fsPromises.readdir()
读取目录的内容。
这段代码读取文件夹的内容,包括文件和子文件夹,并返回它们的相对路径:
const fs = require('fs');
const folderPath = '/Users/joe';
fs.readdirSync(folderPath);
你可以得到完整的路径:
fs.readdirSync(folderPath).map(fileName => {
return path.join(folderPath, fileName);
});
您也可以过滤结果,只返回文件,并排除文件夹:
const isFile = fileName => {
return fs.lstatSync(fileName).isFile();
};
fs.readdirSync(folderPath)
.map(fileName => {
return path.join(folderPath, fileName);
})
.filter(isFile);
重命名文件夹
使用 fs.rename()
或 fs.renameSync()
或 fsPromises.rename()
重命名文件夹。第一个参数是当前路径,第二个是新路径:
const fs = require('fs');
fs.rename('/Users/joe', '/Users/roger', err => {
if (err) {
console.error(err);
}
// done
});
fs.renameSync()
为同步版本:
const fs = require('fs');
try {
fs.renameSync('/Users/joe', '/Users/roger');
} catch (err) {
console.error(err);
}
fsPromises.rename()
是基于Promise的版本:
const fs = require('fs/promises');
async function example() {
try {
await fs.rename('/Users/joe', '/Users/roger');
} catch (err) {
console.log(err);
}
}
example();
删除文件夹
使用 fs.rmdir()
或 fs.rmdirSync()
或 fsPromises.rmdir()
删除文件夹。
const fs = require('fs');
fs.rmdir(dir, err => {
if (err) {
throw err;
}
console.log(`${dir} is deleted!`);
});
要删除包含内容的文件夹,请使用 fs.rm()
和选项 { recursive: true }
以递归方式删除内容。
{ recursive: true, force: true }
使其在文件夹不存在时忽略异常。
const fs = require('fs');
fs.rm(dir, { recursive: true, force: true }, err => {
if (err) {
throw err;
}
console.log(`${dir} is deleted!`);
});