06-访问文件系统
20.5 访问文件系统
很多入门级的编程书都会涵盖文件系统的访问,因为它被认为是“标准”程序设计中一个非常重要的部分。而对于可怜的JavaScript,直到Node的出现才让它加入了可以操作文件系统的行列。
本章中的例子都假设项目根目录是/home/
使用 fs.writeFile
可以创建一个文件。在根目录下创建一个叫作write.js的文件:
const fs = require('fs');
fs.writeFile('hello.txt', 'hello from Node!', function(err) {
if(err) return console.log('Error writing to file.');
});
这段代码会在当前目录下创建一个文件(假设在这个目录下有足够的权限,同时这个目录下不存在一个名为hello.txt的目录或只读文件)。无论何时运行一个Node应用,它的当前工作目录都会继承自运行时所在的目录(可能会与文件所在目录不一样)。比如:
$ cd /home/jdoe/fs
$ node write.js # 当前工作目录是/home/jdoe/fs
# 创建 /home/jdoe/fs/hello.txt
$ cd .. # 当前工作目录是 /home/jdoe
$ node fs/write.js # 创建/home/jdoe/hello.txt
Node提供了一个特殊的变量, __dirname
,它总是会把目录指定为源文件所在的目录。比如,可以这样修改代码:
const fs = require('fs');
fs.writeFile(__dirname + '/hello.txt',
'hello from Node!', function(err) {
if(err) return console.error('Error writing to file.');
});
现在,write.js将始终在/home/
const fs = require('fs');
const path = require('path');
fs.writeFile(path.join(__dirname, 'hello.txt'),
'hello from Node!', function(err) {
if(err) return console.error('Error writing to file.');
});
path.join会把文件路径用平台可识别的连接符连接起来,这通常都是一个好的实践。
如果还想读取文件内容呢?可以用 fs.readFile
。创建read.js文件:
const fs = require('fs');
const path = require('path');
fs.readFile(path.join(__dirname, 'hello.txt'), function(err, data) {
if(err) return console.error('Error reading file.');
console.log('Read file contents:');
console.log(data);
});
运行这段代码后,大家可能会被结果吓到:
Read file contents:
<Buffer 68 65 6c 6c 6f 20 66 72 6f 6d 20 4e 6f 64 65 21>
如果把这些十六进制码转化成对应的ASCII/Unicode,会发现打出来了hello from Node!,但程序本身的输出看起来却不太友好。如果不给fs.readFile指定解码格式,它只会输出含有二进制数据的buffer。即使不给write.js指定编码格式,它也会用默认的UTF-8(一种统一编码方式)。可以通过修改read.txt来指定解码格式为UTF-8,从而得到想要的输出:
const fs = require('fs');
const path = require('path');
fs.readFile(path.join(__dirname, 'hello.txt'),
{ encoding: 'utf8' }, function(err, data) {
if(err) return console.error('Error reading file.');
console.log('File contents:');
console.log(data);
});
fs模块中所有的函数都有与其等价的同步执行方式(以“Sync”结尾)。在write.js中,与它等价的是:
fs.writeFileSync(path.join(__dirname, 'hello.txt'), 'hello from Node!');
而在read.js中则是:
const data = fs.readFileSync(path.join(__dirname, 'hello.txt'),
{ encoding: 'utf8' });
有了这些同步执行的方式,就可以通过exception来处理错误了,为了让代码更加健壮,可以把代码块用try/catch块包起来。比如:
try {
fs.writeFileSync(path.join(__dirname, 'hello.txt'), 'hello from Node!');
} catch(err) {
console.error('Error writing file.');
}
同步的文件系统函数特别好用。不过,如果在编写一个webserver或者网络应用程序,一定要知道,Node的性能来源于异步执行;在这种情况下一定要使用异步的方式。当然,如果在编写命令行应用,使用同步方式一般不会有问题。
可以通过 fs.readdir
来列出当前目录下的所有文件。创建一个名为ls.js的文件:
const fs = require('fs');
fs.readdir(__dirname, function(err, files) {
if(err) return console.error('Unable to read directory contents');
console.log(`Contents of ${__dirname}:`);
console.log(files.map(f => '\t' + f).join('\n'));
});
fs模块包含了很多关于文件系统的函数。通过这些函数可以删除(fs.unlink)、移动、重命名(fs.rename)文件,以及获取文件信息和目录(fs.stat)。想了解更多内容,可以参阅Node API文档(https://nodejs.org/api/fs.html)。