Node.js01--fs模块

Node.js笔记(二)————fs模块

fs模块

Node.js内置的fs模块就是文件系统模块,负责读写文件。和所有其它JavaScript模块不同的是,fs模块同时提供了异步和同步的方法。
什么是异步方法。因为JavaScript的单线程模型,执行IO操作时,JavaScript代码无需等待,而是传入回调函数后,继续执行后续JavaScript代码。比如jQuery提供的getJSON()操作:

1
2
3
4
$.getJSON('http://example.com/ajax', function (data) {
console.log('IO结果返回后执行...');
});
console.log('不等待IO结果直接执行后续代码...');

而同步的IO操作则需要等待函数返回:

1
2
// 根据网络耗时,函数将执行几十毫秒~几秒不等:
var data = getJSONSync('http://example.com/ajax');

同步操作的好处是代码简单,缺点是程序将等待IO操作,在等待时间内,无法响应其它任何事件。而异步读取不用等待IO操作,但代码较麻烦。

异步读文件

异步读取文件的代码如下:

1
2
3
4
5
6
7
8
9
var fs = require('fs');

fs.read('sample.txt', 'utf-8', function (err, data) {
if (err) {
console.log(err);
} else {
console.log(data);
}
});

注意:sample.txt文件必须在当前目录下,且文件编码为utf-8。
异步读取时,传入的回调函数接收两个参数,当正常读取时,err参数为null,data参数为读取到的String。当读取发生错误时,err参数代表一个错误对象,data为undefined。这也是Node.js标准的回调函数:第一个参数代表错误信息,第二个参数代表结果。
由于err是否为null就是判断是否出错的标志,所以通常的判断逻辑总是:

1
2
3
4
5
if (err) {
// 出错了
} else {
// 正常
}

下面的例子演示了如何读取一个图片文件:

1
2
3
4
5
6
7
8
9
10
var fs = require('fs');

fs.read('sample.png', function (err, data) {
if (err) {
console.log(err);
} else {
console.log(data);
console.log(data.length + ' bytes');
}
});

当读取二进制文件时,不传入文件编码时,回调函数的data参数将返回一个Buffer对象。在Node.js中,Buffer对象就是一个包含零个或任意个字节的数组(注意和Array不同)。
Buffer对象可以和String作转换,例如,把一个Buffer对象转换成String:

1
2
3
// Buffer -> String
var text = data.toString('utf-8');
console.log(text);
1
2
3
// String -> Buffer
var buf = Buffer.from(text, 'utf-8');
console.log(buf);

同步读文件

除了标准的异步读取模式外,fs也提供相应的同步读取函数。同步读取的函数和异步函数相比,多了一个Sync后缀,并且不接收回调函数,函数直接返回结果。
用fs模块同步读取一个文本文件的代码如下:

1
2
3
4
var fs = require('fs');

var data = fs.readFileSync('sample.txt', 'utf-8');
console.log(data);

可见,原异步调用的回调函数的data被函数直接返回,函数名需要改为readFileSync,其它参数不变。
如果同步读取文件发生错误,则需要用try…catch捕获该错误:

1
2
3
4
5
6
try {
var data = fs.readFileSync('sample.txt', 'utf-8');
console.log(data);
} catch (err) {
// 出错了
}

写文件

将数据写入文件是通过fs.writeFile()实现的:

1
2
3
4
5
6
7
8
9
10
var fs = require('fs');

var data = 'Hello, Node.js';
fs.writeFile('output.txt', data, function (err) {
if (err) {
console.log(err);
} else {
console.log('ok.');
}
});

writeFile()的参数依次为文件名、数据和回调函数。如果传入的数据是String,默认按UTF-8编码写入文本文件,如果传入的参数是Buffer,则写入的是二进制文件。回调函数由于只关心成功与否,因此只需要一个err参数。

和readFile()类似,writeFile()也有一个同步方法,叫writeFileSync():

1
2
3
4
5
var fs = require('fs');

var data = 'Hello, Node.js';
fs.writeFileSync('output.txt', data);
stat

如果我们要获取文件大小,创建时间等信息,可以使用fs.stat(),它返回一个Stat对象,能告诉我们文件或目录的详细信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var fs = require('fs');

fs.stat('sample.txt', function (err, stat) {
if (err) {
console.log(err);
} else {
// 是否是文件:
console.log('isFile: ' + stat.isFile());
// 是否是目录:
console.log('isDirectory: ' + stat.isDirectory());
if (stat.isFile()) {
// 文件大小:
console.log('size: ' + stat.size);
// 创建时间, Date对象:
console.log('birth time: ' + stat.birthtime);
// 修改时间, Date对象:
console.log('modified time: ' + stat.mtime);
}
}
});

结果:

1
2
3
4
5
isFile: true
isDirectory: false
size: 181
birth time: Fri Dec 11 2015 09:43:41 GMT+0800 (CST)
modified time: Fri Dec 11 2015 12:09:00 GMT+0800 (CST)

stat()也有一个对应的同步函数statSync()。

stats类中的方法有:

1
2
3
4
5
6
7
8
9
方法 描述
stats.isFile() 如果是文件返回 true,否则返回 false
stats.isDirectory() 如果是目录返回 true,否则返回 false
stats.isBlockDevice() 如果是块设备返回 true,否则返回 false
stats.isCharacterDevice() 如果是字符设备返回 true,否则返回 false
stats.isSymbolicLink() 如果是软链接返回 true,否则返回 false
stats.isFIFO() 如果是FIFO,返回true,否则返回
false。FIFO是UNIX中的一种特殊类型的命令管道。
stats.isSocket() 如果是 Socket 返回 true,否则返回 false

创建 file.js 文件,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var fs = require("fs");

console.log("准备打开文件!");
fs.stat('input.txt', function (err, stats) {
if (err) {
return console.error(err);
}
console.log(stats);
console.log("读取文件信息成功!");

// 检测文件类型
console.log("是否为文件(isFile) ? " + stats.isFile());
console.log("是否为目录(isDirectory) ? " + stats.isDirectory());
});

以上代码执行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ node file.js 
准备打开文件!
{ dev: 16777220,
mode: 33188,
nlink: 1,
uid: 501,
gid: 20,
rdev: 0,
blksize: 4096,
ino: 40333161,
size: 61,
blocks: 8,
atime: Mon Sep 07 2015 17:43:55 GMT+0800 (CST),
mtime: Mon Sep 07 2015 17:22:35 GMT+0800 (CST),
ctime: Mon Sep 07 2015 17:22:35 GMT+0800 (CST) }
读取文件信息成功!
是否为文件(isFile) ? true
是否为目录(isDirectory) ? false

异步还是同步

在fs模块中,提供同步方法是为了方便使用。那我们到底是应该用异步方法还是同步方法呢?

由于Node环境执行的JavaScript代码是服务器端代码,所以,绝大部分需要在服务器运行期反复执行业务逻辑的代码,必须使用异步代码,否则,同步代码在执行时期,服务器将停止响应,因为JavaScript只有一个执行线程。

服务器启动时如果需要读取配置文件,或者结束时需要写入到状态文件时,可以使用同步代码,因为这些代码只在启动和结束时执行一次,不影响服务器正常运行时的异步执行。

打开文件

1
fs.open(path, flags[, mode], callback)
  • path - 文件的路径。
  • flags - 文件打开的行为。
  • mode - 设置文件模式(权限),文件创建默认权限为 0666(可读,可写)。
  • callback - 回调函数,带有两个参数如:callback(err, fd)。
  • flags 参数可以是以下值:

Flag 描述

  • r 以读取模式打开文件。如果文件不存在抛出异常。
  • r+ 以读写模式打开文件。如果文件不存在抛出异常。
  • rs 以同步的方式读取文件。
  • rs+ 以同步的方式读取和写入文件。
  • w 以写入模式打开文件,如果文件不存在则创建。
  • wx 类似 ‘w’,但是如果文件路径存在,则文件写入失败。
  • w+ 以读写模式打开文件,如果文件不存在则创建。
  • wx+ 类似 ‘w+’, 但是如果文件路径存在,则文件读写失败。
  • a 以追加模式打开文件,如果文件不存在则创建。
  • ax 类似 ‘a’, 但是如果文件路径存在,则文件追加失败。
  • a+ 以读取追加模式打开文件,如果文件不存在则创建。
  • ax+ 类似 ‘a+’, 但是如果文件路径存在,则文件读取追加失败。

创建 file.js 文件,并打开 input.txt 文件进行读写,代码如下所示:

1
2
3
4
5
6
7
8
9
10
var fs = require("fs");

// 异步打开文件
console.log("准备打开文件!");
fs.open('input.txt', 'r+', function(err, fd) {
if (err) {
return console.error(err);
}
console.log("文件打开成功!");
});

以上代码执行结果如下:

1
2
3
4
5
$ node file.js 
准备打开文件!
文件打开成功!
关闭文件
fs.close(fd, callback)

参数使用说明如下:

1
2
1.fd - 通过 fs.open() 方法返回的文件描述符。
2.callback - 回调函数,没有参数

截取文件

1
2
3
4
fs.ftruncate(fd, len, callback)
1.fd - 通过 fs.open() 方法返回的文件描述符。
2.len - 文件内容截取的长度。
3.callback - 回调函数,没有参数。

删除文件

1
2
3
fs.unlink(path, callback)
1.path - 文件路径。
2.callback - 回调函数,没有参数。
1
2
3
4
5
6
7
8
9
var fs = require("fs");

console.log("准备删除文件!");
fs.unlink('input.txt', function(err) {
if (err) {
return console.error(err);
}
console.log("文件删除成功!");
});

以上代码执行结果如下:

1
2
3
4
$ node file.js 
准备删除文件!
文件删除成功!
创建目录
1
2
3
4
5
6
fs.mkdir(path[, options], callback)
1.path - 文件路径。
2.options 参数可以是:
recursive - 是否以递归的方式创建目录,默认为 false
mode - 设置目录权限,默认为 0777
3.callback - 回调函数,没有参数。
1
2
3
4
5
6
7
8
9
var fs = require("fs");
// tmp 目录必须存在
console.log("创建目录 /tmp/test/");
fs.mkdir("/tmp/test/",function(err){
if (err) {
return console.error(err);
}
console.log("目录创建成功。");
});

以上代码执行结果如下:

1
2
3
4
$ node file.js 
创建目录 /tmp/test/
目录创建成功。
读取目录

fs.readdir(path, callback)
1.path - 文件路径。
2.callback - 回调函数,回调函数带有两个参数err, files,err 为错误信息,files 为 目录下的文件数组列表。
创建 file.js 文件,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
var fs = require("fs");

console.log("查看 /tmp 目录");
fs.readdir("/tmp/",function(err, files){
if (err) {
return console.error(err);
}
files.forEach( function (file){
console.log( file );
});
});

以上代码执行结果如下:

1
2
3
4
$ node file.js 
查看 /tmp 目录
input.out
output.out

test
test.txt
删除目录
fs.rmdir(path, callback)
1.path - 文件路径。
2.callback - 回调函数,没有参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var fs = require("fs");
// 执行前创建一个空的 /tmp/test 目录
console.log("准备删除目录 /tmp/test");
fs.rmdir("/tmp/test",function(err){
if (err) {
return console.error(err);
}
console.log("读取 /tmp 目录");
fs.readdir("/tmp/",function(err, files){
if (err) {
return console.error(err);
}
files.forEach( function (file){
console.log( file );
});
});
});

以上代码执行结果如下:

1
2
3
4
$ node file.js 
准备删除目录 /tmp/test
读取 /tmp 目录
……
-------------本文结束感谢您的阅读-------------