NodeJS(④)——文件系统

文件系统,即fs(file system)模块,是nodejs模块中非常重要的一个模块。javascript无法对文件进行操作,但是nodejs中的fs模块可以。

文件系统主要分成三部分:

1.文件操作

2.文件夹操作

3.流操作

文件操作

文件操作的api如下:

fs.readFile(file[,options],callback)
fs.readFileSync(file[,options]) 同步读取
fs.writeFile(file, data[,options],callback)
fs.writeFileSync(file, data[,option])
fs.stat(path, callback)
fs.statSync(path)

参数中的file是我们要读/写的文件路径,option是选择编码方式,默认是二进制编码,我们一般写“utf-8”,最后的callback就是回调函数,当文件读/写成功之后执行。

前四个api中带有Sync的方法是同步执行,同步执行的函数是没有回调函数的。


'use strict';
const fs = require('fs');
fs.readFile('./data.txt', 'utf-8', function (err, data) {
	if(err) {
		console.log(err);
		return ;
	}
	console.log(data);
})
// hello world

'use strict';
let data = fs.readFileSync('./data.txt', 'utf-8');
console.log('sync:',data);

同步执行的结果总是在异步执行结果之前。

如果同步执行中出现了错误,会导致整个线程卡死,因此我们一般不使用同步的方法,而是用异步的方法,这也和nodejs异步的特性一致。

 

write方法的目标如果已经存在了,那么会先删除这个文件,再新建一个新的文件,也就是替换掉原来的文件。


'use strict';

const fs = require('fs');


fs.readFile('./data.txt', 'utf-8', function (err, data) {
	if(err) {
		console.log(err);
		return ;
	}
	console.log(data);
	fs.writeFile('./dest.txt', data+' --by earth', 'utf-8', function (err) {
		if(err) {
			console.log(err);
			return ;
		}
		console.log('write success!');
	})
})

同样的,同步版本的write也尽量少用。

fs.stats方法可以返回一个stat对象,这个对象同样有一些方法:

stats.isFile()
stats.isDirectory()
stats.isBlockDevice()
stats.isCharacterDevice()
stats.isSymbolLink()
stats.isFIFO()
stats.isSocket()

我们一般使用stat对象的前两个方法,用来判断是否是一个文件,或者是否是一个文件夹。


'use strict';
const fs = require('fs');
const fileName = './data.txt';
const state = fs.statSync(fileName);

if(state.isFile()) {
	fs.readFile(fileName, 'utf-8', function (err, data) {
		if(err) {
			console.log(err);
			return ;
		}
		console.log(data);
		fs.writeFile('./dest.txt', data+' --by earth', 'utf-8', function (err) {
			if(err) {
				console.log(err);
				return ;
			}
			console.log('write success!');
		})
	})
}else {
	console.log(fileName ,'is a directory');
}

文件夹操作

文件夹操作的api如下:

fs.mkdir(path[,mode], callback)

fs.mkdirSync(path[,mode])

fs.readdir()

fs.readdirSync()

mode是一个权限功能,这里不做过多介绍,一般情况下不去操作这个mode。

Sync同样是属于同步的方法,尽量少用。



'use strict';

const fs = require('fs');
const fileName = './data.txt';
const state = fs.statSync(fileName);

if(state.isFile()) {
	fs.readFile(fileName, 'utf-8', function (err, data) {
		if(err) {
			console.log(err);
			return ;
		}
		console.log(data);
		fs.mkdir('./mydir',() => {
			fs.writeFile('./mydir/dest.txt', data+' --by earth', 'utf-8', 

function (err) {
				if(err) {
					console.log(err);
					return ;
				}
				console.log('write success!');
			})
		})
			
	})
}else {
	console.log(fileName ,'is a directory');
}

练习:

读取文件并且以树形结构显示出来。

1.深度优先遍历(dfs)

深度优先遍历是先将一个文件夹全部遍历完之后再去遍历下一个文件夹。

这个方式的遍历需要用到同步读取文件的方法。


'use strict';
const fs = require('fs');
let filePath = './dir';
function dfs(path, num) {
	let data = fs.readdirSync(path, 'utf-8');
	for(let i = 0; i < data.length; i++) {
		let _path = path + '/' + data[i]
		const state = fs.statSync(_path);
		let fuhao = '+'
		for(let j = 1; j < num * 3; j++) {
			fuhao += '-';
		}
		console.log(fuhao, data[i]);
		if(state.isDirectory()) {
			dfs(_path, num + 1);
		}else {
			return ;
		}
	}
}

dfs(filePath, 0);

dfs

2.广度优先遍历(bfs)

广度优先遍历是先遍历完一层的文件,再去遍历下一层的文件。

这个方法使用异步的文件读取即可。


'use strict';

const fs = require('fs');
let filePath = './dir';
function bfs(path, num) {
	fs.readdir(path, 'utf-8', function (err, data) {
		if(err) {
			console.log(err);
			return ;
		}
		for(let i = 0; i < data.length; i++) {
			// console.log(path + '/' + data[i]);
			let _path = path + '/' + data[i]
			const state = fs.statSync(_path);
			let fuhao = '+'
			for(let j = 1; j < num * 3; j++) {
				fuhao += '-';
			}
			console.log(fuhao, data[i]);
			if(state.isDirectory()) {
				bfs(_path, num + 1);
			}
		}
	})
}

bfs(filePath, 0);
	

bfs

流操作

流操作是用来解决内存不足的问题的,学过操作系统的同学应该都清楚。

当我们文件的大小超过了我们计算机内存大小的时候,我们就不可能一次性将这个文件读取到硬盘中了,这个时候只能将文件分成好几次读取,每一次的大小都是我们内存可以承受的大小即可。

流操作的api如下:

fs.createReadStream(path[,options])读取流操作
fs.createWriteStream(path[,options])写入流操作

其中的option有很多选项,可以上官网查阅。

默认的编码方式是utf-8。


'use strict';
const rs = fs.createReadStream('./data.txt',{
	encoding: 'utf-8',
	start: 5,
	end: 10
});

因为是流水式读取,每次只读取大概16kb的内容,因此并没有回调函数。

这样,我们为了知道什么时候文件读取完毕,需要用到我们的事件绑定系统


'use strict';

const fs = require('fs');

const rs = fs.createReadStream('./data.txt','utf-8');

rs.on('data', function(data) {
	console.log('+++++');
	console.log(data);
	console.log('+++++');
})

rs.on('end', function () {
	console.log('finish read');
})

这里我们用到了两个事件:data事件和end事件

data事件是在当我们读取一次数据之后就会自动触发。

end事件是当我们文件全部读取完毕之后会自动触发。

除了这两个事件之外,readable对象还支持readable事件和error事件。

在data触发之前会触发readable事件,这个事件表示有可以读取的内容,但是不会读取数据,因此我们如果要使用readable来读取数据的话,需要我们手动把数据读取出来。


'use strict';

const fs = require('fs');

const rs = fs.createReadStream('./data.txt',{
	encoding: 'utf-8',
	start: 5,
	end: 10
});
let thunk = '';
rs.on('readable', function () {
	let data = '';
	while(true) {
		data = rs.read();
		if(data == null) {
			return;
		}else {
			thunk += data;
		}
	}
})

rs.on('end', function () {
	console.log(thunk);
})

这一段代码可以实现和上面那段代码一样的功能。

我们在这一段代码中发现了一个rs.read()方法。这里就再介绍一下readable对象上的方法:

rs.pause() 可以让读取中途暂停
rs.resume() 让暂停的读取继续开始
rs.read() 读取当前的数据,当没有数据的时候返回null。

 

写入的流操作和读取流操作基本是一样的。

writable对象同样有两个方法:

1.ws.write(chunk[,encoding][,callback]) 写入

2.ws.end() 写入结束


'use strict';

const fs = require('fs');

const rs = fs.createReadStream('./data.txt');
const ws = fs.createWriteStream('./write.txt', {
	flag: ''
});

let thunk = '';
rs.on('data', function (data) {
	ws.write(data)
})
rs.on('end', function () {
	ws.end();
})

默认是先删除再写入,也就是覆盖文件的原本内容。

我们可以通过改变option中的flags参数,来改变写入方式。


const ws = fs.createWriteStream('./write.txt', {
	flags: 'a'
});

这样写入的内容就是拼接在源文件后面的。

上面的代码中,我们同时创建了读取流和写入流,然后一边读取一边写入,代码写起来比较繁琐。

nodejs给写入提供了一套接口,本质和我们刚才写的代码是一样的,但是用起来会非常方便。


'use strict';

const fs = require('fs');

const rs = fs.createReadStream('./data.txt');
const ws = fs.createWriteStream('./write.txt', {
	flags: 'a'
});
rs.pipe(ws);

通过使用pipe方法,我们就可以把读取到的内容写入到我们的参数ws中了。

文件监测接口

nodejs还提供了一个文件监测接口:watch方法。


'use strict';
fs.watch(filename[,option][,listener])

fs.watch('./write.txt', function (eventType, filename) {
	console.log(eventType, filename, 'file change');
})

这样当文件发生改变的时候,watch方法就会自动调用,不论是改变内容还是改变文件名。

不过需要注意的是,当文件名发生改变的时候,监听一次后这个方法就是暂时失效,因为我们的方法只绑定在原来的名为write.txt的文件上。

共有 0 条评论

Top