前言
Node.js 中赋予了 JavaScript 很多在浏览器中没有的能力,譬如:文件读写,创建 http 服务器等等,今天我们就来看看在 node 中怎样用 JavaScript 进行文件的读写操作。
读文件
- 我们新建一个
hello.txt
,并且在里面写入:hello, node.js!!
,如图:
- 我们在
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()
读取文件成功:data为文件内容,error为null,读取失败:error为错误对象,data为undefined
|
最后我们hello.js
中的代码如下:
1 2 3 4 5 6
| var fs = require('fs')
var content = fs.readFileSync('hello.txt', { flag: 'r' }) console.log(content.toString())
|
在这里可以说一下,我们读取回来的默认是二进制的内容,所以需要调用toString()
方法进行转换。最后,终端可以看到结果如下:
1 2
| var content = fs.readFileSync('hello.txt', { flag: 'r', encoding: 'utf-8' })
|
上面是采用同步的方式来读取文件,我们也可以使用异步的方式来进行读取。
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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')
|
可以看到我们刚才在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))
|
我们在读取多个文件的时候也可以这样使用async await
的方法。我们现在有 3 个文件:
他们的内容分别是hello:hello2
,hell2:hello3
,hello3:我们最终读取的文件
, 这样的话我们必须依次读取文件才能读取到最后的文件。
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()
|
注意:这里我使用了 vscode 编辑器,根据我的代码风格,编辑器每次都会自动在代码末尾添加一行空行,这会导致输出的时候会多出一个空格来,所以我使用了trim()
方法去掉空格。
写文件
我们新建一个文件write.js
中写入下面这行代码,并在同级目录下新建一个test.txt
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| let fs = require('fs') fs.writeFile( 'test.txt', '今天晚上吃什么', { flag: 'w', encoding: 'utf-8' }, function (err) { if (err) { console.log('写入出错') } else { console.log('写入成功') } } )
|
但是这样的话,我们再继续写入的话,会发现原先的内容会被覆盖掉:
而我们本来的目的应该是,在后面追加一个内容,这时候我们只需要修改flag
为a
就好!
1 2 3 4 5 6 7 8 9 10 11
| 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 14 15
| function fsWrite(path, content) { return new Promise(function (resolve, reject) { fs.writeFile(path, content, { flag: 'a', encoding: 'utf-8' }, function ( err ) { if (err) { reject(err) } else { 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') }
|
删除文件
语法
以下为删除文件的语法格式:
1
| fs.unlink(path, callback)
|
参数
参数使用说明如下:
- path - 文件路径。
- callback - 回调函数,没有参数。
实例
input.txt 文件内容为:
接下来我们创建 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')
console.log('创建目录 /tmp/test/') fs.mkdir('/tmp/test/', function (err) { if (err) { return console.error(err) } console.log('目录创建成功。') })
|
以上代码执行结果如下:
可以添加 recursive: true 参数,不管创建的目录 /tmp 和 /tmp/test 是否存在:
1 2 3
| fs.mkdir('/tmp/a/apple', { recursive: true }, (err) => { if (err) throw err })
|
读取目录
语法
以下为读取目录的语法格式:
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 35 36
| 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) { reject(err) } else { 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')
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
| var readline = require('readline')
var rl = readline.createInterface({ input: process.stdin, output: process.stdout, })
rl.question('今天晚上吃什么?', function (answer) { console.log('答案:' + answer) rl.close() })
rl.on('close', function () { process.exit(0) })
|
这样的话,我们就可以通过提问的方式来创建一个文件:
首先为了避免嵌套的问题,我们还是先对其进行封装一下:
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')
let readline = require('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.js,我们的 JavaScript 才有了无限的可能,使得 JavaScript 不单单局限在浏览器窗口,俗话说得好:‘能用 JavaScript 来实现的,最终都会用 JavaScript 来实现’。