Node中核心模块fs模块和输入输出

前言

Node.js中赋予了JavaScript很多在浏览器中没有的能力,譬如:文件读写,创建http服务器等等,今天我们就来看看在node中怎样用JavaScript进行文件的读写操作。

读文件

  1. 我们新建一个hello.txt,并且在里面写入:hello, node.js!! ,如图:

node演示

  1. 我们在hello.txt同级目录下创建一个hello.js文件,我们在这个js文件中利用Node提供的文件操作API, 读取hello.txt文件中的内容。
  • node中对文件相关的操作需要依赖fs模块,这个是node中内置模块之一,我们需要引入。fs–file system。
1
2
3
4
let fs = require('fs')
fs.readFile()
// 读文件。 readFile函数接受两个参数:读取文件路径,回调函数(error,data两个参数),
读取文件成功:data为文件内容,error为null,读取失败:error为错误对象,data为undefined

最后我们hello.js中的代码如下:

1
2
3
4
5
6
var fs = require('fs')
// 导入文件模块
// node读写文件也有同步和异步的接口
// 同步的方式
var content = fs.readFileSync('hello.txt',{flag:'r'})
console.log(content.toString());

在这里可以说一下,我们读取回来的默认是二进制的内容,所以需要调用toString()方法进行转换。最后,终端可以看到结果如下:

node演示

1
2
// 你也可以直接加上编码,这样就不需要再调用toString()方法了
var content = fs.readFileSync('hello.txt',{flag:'r',encoding:'utf-8'})

上面是采用同步的方式来读取文件,我们也可以使用异步的方式来进行读取。

1
2
3
4
5
6
7
8
9
10
// 异步
fs.readFile("hello.txt",{flag:'r',encoding:'utf-8'},function(err, data){
if(err) {
console.log(err);
} else {
console.log(data);
}
console.log('456');
})
console.log('123');

node演示

可以看到我们刚才在hello.txt中写入的文本hello, node.js!!已经打印出来。看到这里是不是觉得很牛叉,JavaScript居然可以用来读取文件内容,完全颠覆了我们以前对JavaScript的理解,然而这一切都得归功于Node.js。

但是如果我们需要读取多个文件的时候,就会出现一个嵌套回调的问题,我们可以对他进行一个简单的封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 封装
function fsRead(path) {
return new Promise(function(resolve, reject) {
fs.readFile(path,{flag:'r',encoding:'utf-8'},function(err, data){
if(err) {
// 失败执行的内容
reject(err)
} else {
// 成功执行的内容
resolve(data)
}
})
})
}

封装成一个promise对象后,我们就可以解决掉回调的问题,也可以直接使用.then 方法。

1
2
var w1 = fsRead('hello.txt')
w1.then(res => console.log(res))

node演示

我们在读取多个文件的时候也可以这样使用async await 的方法。我们现在有3个文件:

文件目录

他们的内容分别是hello:hello2 ,hell2:hello3hello3:我们最终读取的文件, 这样的话我们必须依次读取文件才能读取到最后的文件。

1
2
3
4
5
6
7
async function readList() {
var file2 = await fsRead('hello.txt')
var file3 = await fsRead(file2.trim() + '.txt')
var file3Content = await fsRead(file3.trim() + '.txt')
console.log(file3Content);
}
readList()

node演示

注意:这里我使用了vscode编辑器,根据我的代码风格,编辑器每次都会自动在代码末尾添加一行空行,这会导致输出的时候会多出一个空格来,所以我使用了trim()方法去掉空格。

自动换行

写文件

我们新建一个文件write.js中写入下面这行代码,并在同级目录下新建一个test.txt 文件:

1
2
3
4
5
6
7
8
9
let fs = require('fs')
fs.writeFile('test.txt',"今天晚上吃什么", {flag:'w', encoding:'utf-8'},function(err){
if(err) {
console.log('写入出错');
}else{
console.log('写入成功');
}
})
// 写文件。writeFile接受三个参数:写入文件路径,写入内容,回调函数。

node演示

写入成功

但是这样的话,我们再继续写入的话,会发现原先的内容会被覆盖掉:

写入覆盖

而我们本来的目的应该是,在后面追加一个内容,这时候我们只需要修改flaga 就好!

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

fs.writeFile('test.txt',"红烧肉", {flag:'a', encoding:'utf-8'},function(err){
if(err) {
console.log('写入出错');
}else{
console.log('写入成功');
}
})

追加内容

但是这也是一个异步的操作,有时候我们也会出现一些问题,不然就必须嵌套,比如我们希望得到的是一个对话,今晚吃啥,红烧肉,红烧肉不好吃:

同步异步问题

但是我们得到的并不是一个我们想要的结果。我们可以进行一个封装:

1
2
3
4
5
6
7
8
9
10
11
12
13
function fsWrite(path,content) {
return new Promise(function(resolve,reject){
fs.writeFile(path, content, {flag:'a', encoding:'utf-8'},function(err){
if(err) {
// console.log('写入出错');
reject(err)
}else{
// console.log('写入成功');
resolve('写入成功')
}
})
})
}

这样我们我们就可以达到之前预期的效果了:

1
2
3
4
5
async function writeList() {
await fsWrite('test.txt','1:早餐吃什么?\n')
await fsWrite('test.txt','2:午餐吃什么?\n')
await fsWrite('test.txt','3:晚餐吃什么?\n')
}

node演示

删除文件

语法

以下为删除文件的语法格式:

1
fs.unlink(path, callback)

参数

参数使用说明如下:

  • path - 文件路径。
  • callback - 回调函数,没有参数。

实例

input.txt 文件内容为:

1
site:blog.juanertu.com

接下来我们创建 file.js 文件,代码如下所示:

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
$ node file.js 
准备删除文件!
文件删除成功!

再去查看 input.txt 文件,发现已经不存在了。而且回收站也找不到这个文件,所以没事别瞎删……


创建目录

语法

以下为创建目录的语法格式:

1
fs.mkdir(path[, options], callback)

参数

参数使用说明如下:

  • path - 文件路径。
  • options 参数可以是:
    • recursive - 是否以递归的方式创建目录,默认为 false。
    • mode - 设置目录权限,默认为 0777。
  • 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
$ node index.js 
目录创建成功。

可以添加 recursive: true 参数,不管创建的目录 /tmp 和 /tmp/test 是否存在:

1
2
3
fs.mkdir('/tmp/a/apple', { recursive: true }, (err) => {
if (err) throw err;
});

node演示

读取目录

语法

以下为读取目录的语法格式:

1
fs.readdir(path, callback)

参数

参数使用说明如下:

  • path - 文件路径。
  • callback - 回调函数,回调函数带有两个参数err, files,err 为错误信息,files 为 目录下的文件数组列表。

实例

代码如下所示:

1
2
3
4
5
6
7
8
let fs = require('fs')
fs.readdir('../day02',function(err,files) {
if(err) {
console.log(err);
}else{
console.log(files);
}
})

以上代码执行结果如下:

读取目录

我们还可以进行一些操作:

首先我们新建一个rw.js 文件,里面放着我们之前封装好的读取和写入函数,并导出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
let fs = require('fs')

function fsRead(path) {
return new Promise(function(resolve, reject) {
fs.readFile(path,{flag:'r',encoding:'utf-8'},function(err, data){
if(err) {
// 失败执行的内容
reject(err)
} else {
// 成功执行的内容
resolve(data)
}
})
})
}

function fsWrite(path,content) {
return new Promise(function(resolve,reject){
fs.writeFile(path, content, {flag:'a', encoding:'utf-8'},function(err){
if(err) {
// console.log('写入出错');
reject(err)
}else{
// console.log('写入成功');
resolve('写入成功')
}
})
})
}

module.exports = {
fsRead,
fsWrite
}

我们在index.js 文件中引入,我们要的操作就是读取test文件夹中的文件,并将这些文件的内容全部写入到all.txt 文件中来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let fs = require('fs')
let {fsRead, fsWrite} = require('./rw')

const txtPath = 'all.txt'
fs.readdir('../day02',function(err,files) {
if(err) {
console.log(err);
}else{
console.log(files);
files.forEach(async function(fileName,i){
let content = await fsRead('../day02/' + fileName)
await fsWrite(txtPath,content);
})
}
})

读取与写入


删除目录

语法

以下为删除目录的语法格式:

1
fs.rmdir(path, callback)

参数

参数使用说明如下:

  • path - 文件路径。
  • callback - 回调函数,没有参数。

实例

接下来我们创建 file.js 文件,代码如下所示:

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
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 引入readline模块
var readline = require('readline');

//创建readline接口实例
var rl = readline.createInterface({
input:process.stdin,
output:process.stdout
});

// question方法
rl.question("今天晚上吃什么?",function(answer){
console.log("答案:"+answer);
// 不加close,则程序不会结束
rl.close();
});

// close事件监听
rl.on("close", function(){
// 结束程序
process.exit(0);
})

node演示

这样的话,我们就可以通过提问的方式来创建一个文件:

首先为了避免嵌套的问题,我们还是先对其进行封装一下:

1
2
3
4
5
6
7
function lcQuestion(question) {
return new Promise(function(resolve, reject){
r1.question(question, function(answer){
resolve(answer)
})
})
}

然后我们就正式开始操作,比如我准备简历一个json文件,包含我的网站信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
let fs = require('fs')
let {fsRead, fsWrite} = require('./rw')
// 导入readline
let readline = require('readline')
//创建readline接口实例
var r1 = readline.createInterface({
input:process.stdin,
output:process.stdout
})
function lcQuestion(question) {
return new Promise(function(resolve, reject){
r1.question(question, function(answer){
resolve(answer)
})
})
}
async function createJson() {
let name = await lcQuestion('您的网站名称?')
let link = await lcQuestion('您的网站地址?')
let description = await lcQuestion('您的网站描述?')
let content = await `{
"name": "${name}",
"link": "${link}",
"description": "${description}"
}`
await fsWrite('site.json',content)
await r1.close()
}
r1.on('close',function(){
// 结束
process.exit(0)
})
createJson()

运行:

node演示

后话

到了这里,我们是不是对node有了一个基本的了解,知道node是干什么的,而且知道正是由于node.js,我们的JavaScript才有了无限的可能,使得JavaScript不单单局限在浏览器窗口,俗话说得好:‘能用JavaScript来实现的,最终都会用JavaScript来实现’。

------ 本文结束  感谢阅读 ------