NodeJS不仅能做网络编程,而且能够操作文件。
var fs = require('fs');
function copy(src, dst) {
fs.writeFileSync(dst, fs.readFileSync(src));
}
function main(path) {
copy(path[0], path[1]);
}
main(process.argv.slice(2));
process
是一个全局变量,可通过process.argv
获得命令行参数。argv[0]
固定等于NodeJS执行程序的绝对路径,argv[1]
固定等于主模块的绝对路径,因此第一个命令行参数从argv[2]
这个位置开始。fs.readFileSync
从源路径读取文件内容,并使用fs.writeFileSync
将文件内容写入目标路径;var fs = require('fs');
function copy(src, dst) {
fs.createReadStream(src).pipe(fs.createWriteStream(dst));
}
function main(path) {
copy(path[0], path[1]);
}
main(process.argv.slice(2));
fs.createReadStream
创建了一个源文件的只读数据流,并使用fs.createWriteStream
创建了一个目标文件的只写数据流pipe
方法把两个数据流连接了起来。var bin = new Buffer([ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]);
Buffer
与字符串类似,除了可以用.length
属性得到字节长度外,还可以用[index]
方式读取指定位置的字节:bin[0]; // => 0x68;
Buffer
与字符串能够互相转化, 可以使用指定编码将二进制数据转化为字符串:var str = bin.toString('utf-8'); // => "hello"
var bin = new Buffer('hello', 'utf-8'); // => <Buffer 68 65 6c 6c 6f>
Buffe
r与字符串有一个重要区别
Buffer
更像是可以做指针操作的C语言数组。例如,可以用 [index]
方式直接修改某个位置的字节。bin[0] = 0x48;
.slice
方法也不是返回一个新的Buffer,而更像是返回了指向原Buffer中间的某个位置的指针[ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]
^ ^
| |
bin bin.slice(2)
.slice
方法返回的Buffer
的修改会作用于原Buffer
var bin = new Buffer([ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]);
var sub = bin.slice(2);
sub[0] = 0x65;
console.log(bin); // => <Buffer 68 65 65 6c 6f>
Buffer
,得首先创建一个新的Buffer
,并通过.copy
方法把原Buffer中
的数据复制过去。这个类似于申请一块新的内存,并把已有内存中的数据复制过去var bin = new Buffer([ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]);
var dup = new Buffer(bin.length);
bin.copy(dup);
dup[0] = 0x48;
console.log(bin); // => <Buffer 68 65 6c 6c 6f>
console.log(dup); // => <Buffer 48 65 65 6c 6f>
Stream
来提供对数据流的操作。Stream
基于事件机制工作,所有Stream
的实例都继承于NodeJS提供的EventEmitter。copy
函数中var rs = fs.createReadStream(pathname);
rs.on('data', function (chunk) {
doSomething(chunk);
});
rs.on('end', function () {
cleanUp();
});
var rs = fs.createReadStream(src);
rs.on('data', function (chunk) {
rs.pause();
doSomething(chunk, function () {
rs.resume();
});
});
rs.on('end', function () {
cleanUp();
});
给doSomething函数加上了回调,因此可以在处理数据前暂停数据读取,并在处理数据后继续读取数据。
可以为数据目标创建一个只写数据流
var rs = fs.createReadStream(src);
var ws = fs.createWriteStream(dst);
rs.on('data', function (chunk) {
ws.write(chunk);
});
rs.on('end', function () {
ws.end();
});
.write
方法的返回值来判断传入的数据是写入目标了,还是临时放在了缓存了,并根据drain
事件来判断什么时候只写数据流已经将缓存中的数据写入目标,可以传入下一个待写数据了。var rs = fs.createReadStream(src);
var ws = fs.createWriteStream(dst);
rs.on('data', function (chunk) {
if (ws.write(chunk) === false) {
rs.pause();
}
});
rs.on('end', function () {
ws.end();
});
ws.on('drain', function () {
rs.resume();
});
NodeJS通过fs
内置模块提供对文件的操作。fs
模块提供的API基本上可以分为以下三类:
文件属性读写: 常用的有fs.stat
、fs.chmod
、fs.chown
等等。
文件内容读写: 常用的有fs.readFile
、fs.readdir
、fs.writeFile
、fs.mkdir
等等。
底层文件操作: 常用的有fs.open
、fs.read
、fs.write
、fs.close
等等。
NodeJS最精华的异步IO模型在fs模块里有着充分的体现, 以fs.readFile为例:
fs.readFile(pathname, function (err, data) {
if (err) {
// Deal with error.
} else {
// Deal with data.
}
});
//基本上所有fs模块API的回调参数都有两个。第一个参数在有错误发生时等于异常对象,第二个参数始终用于返回API方法执行结果。
try {
var data = fs.readFileSync(pathname);
// Deal with data.
} catch (err) {
// Deal with error.
}
NodeJS提供了path内置模块来简化路径相关操作,并提升代码可读性。
path.normalize
: 将传入的路径转换为标准路径;除了解析路径中的.与..外,还能去掉多余的斜杠。如果有程序需要使用路径作为某些数据的索引,但又允许用户随意输入路径时,就需要使用该方法保证路径的唯一性。
var path = require('path');
var cache = {};
function store(key, value) {
cache[path.normalize(key)] = value;
}
store('foo/bar', 1);
store('foo//baz//../bar', 2);
console.log(cache); // => { "foo/bar": 2 }
// 标准化之后的路径里的斜杠在Windows系统下是\,而在Linux系统下是/。如果想保证任何系统下都使用/作为路径分隔符的话,需要用.replace(/\\/g, '/')再替换一下标准路径。
path.join
: 将传入的多个路径拼接为标准路径。path.join('foo/', 'baz/', '../bar'); // => "foo/bar"
path.extname
: 需要根据不同文件扩展名做不同操作时 path.extname('foo/bar.js'); // => ".js"