当前位置:嗨网首页>书籍在线阅读

06-访问文件系统

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

20.5 访问文件系统

很多入门级的编程书都会涵盖文件系统的访问,因为它被认为是“标准”程序设计中一个非常重要的部分。而对于可怜的JavaScript,直到Node的出现才让它加入了可以操作文件系统的行列。

本章中的例子都假设项目根目录是/home//fs,这是一个典型的Unix系统(将替换成开发人员自己设定的用户名)。这个原则在Window操作系统同样适用(项目根目录可能是C:\Users\\Documents\fs)。

使用 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//fs(也就是write.js所在的目录)目录下创建hello.txt。使用字符串连接把__dirname和文件名拼接在一起并不是一个好的做法,因为这样做就使得路径名与运行平台有关联了,比如,这个路径名在Windows上就会有问题。Node的path模块提供了平台无关的路径名,所以可以重写这个模块,让它在所有平台上都能正常工作:

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)。