一、简介
Node是JavaScript语言的服务器运行环境。所谓“运行环境”有两层意思:首先,JavaScript语言通过Node在服务器运行,其次,Node提供大量工具库,使得JavaScript语言与操作系统互动(比如读写文件、新建子进程),在这个意义上,Node又是JavaScript的工具库。
node.js特性其实是JS的特性:事件驱动、非阻塞I/O。
二、Buffer对象
1.概述
Buffer是node处理二进制数据的接口,为全局对象,不需要引入。是一个构造函数,生成一个类似数组的对象。
var bytes = new Buffer(256);
2.创建 Buffer 类
- Buffer.alloc(size[, fill[, encoding]]),返回一个指定大小的 Buffer 实例,如果没有设置 fill,则默认填满 0。
// 创建一个长度为 10、且用 0x1 填充的 Buffer。
const buf2 = Buffer.alloc(10, 1)
- Buffer.concat(list[, totalLength]),返回一个新值Buffer,该值是将所有Buffer 实例连接list在一起的结果。
- Buffer.from(array),返回一个被 array 的值初始化的新的 Buffer 实例(传入的 array 的元素只能是数字,不然就会自动被 0 覆盖)
- Buffer.from(string[, encoding]),将string转换为buffer。
- buf.write(string[, offset[, length]][, encoding]),写入缓冲区,返回实际写入的大小。
buf = Buffer.alloc(256);
len = buf.write("www.runoob.com");
- buf.toString([encoding[, start[, end]]]),读取 Node 缓冲区数据。返回值:解码缓冲区数据并使用指定的编码返回字符串。
const buf = Buffer.alloc(10,'123');
buf.write('jucious')
console.log(buf.toString())
//新写入的字符会覆盖之前的
- Buffer.concat(list[, totalLength])
var buffer1 = Buffer.from(('菜鸟教程'));
var buffer2 = Buffer.from(('www.runoob.com'));
var buffer3 = Buffer.concat([buffer1,buffer2]);
三、EventEmitter
Node.js 里面的许多对象都会分发事件:一个 net.Server 对象会在每次有新连接时触发一个事件, 一个 fs.readStream 对象会在文件被打开的时候触发一个事件。 所有这些产生事件的对象都是 events.EventEmitter 的实例。
- EventEmitter 类
events 模块只提供了一个对象: events.EventEmitter。EventEmitter 的核心就是事件触发与事件监听器功能的封装。
var EventEmitter = require('events').EventEmitter;
var event = new EventEmitter();
event.on('some_event', function() {
console.log('some_event 事件触发');
});
setTimeout(function() {
event.emit('some_event');
}, 1000);
四、Stream(流)
- 概述
”数据流“(stream)是处理系统缓存的一种方式。操作系统采用数据块(chunk)的方式读取数据,每收到一次数据,就存入缓存。Node应用程序有两种缓存的处理方式,第一种是等到所有数据接收完毕,一次性从缓存读取,这就是传统的读取文件的方式;第二种是采用“数据流”的方式,收到一块数据,就读取一块,即在数据还没有接收完成时,就开始处理它。
数据流接口最大特点就是通过事件通信,具有readable、writable、drain、data、end、close等事件,既可以读取数据,也可以写入数据。读写数据时,每读入(或写入)一段数据,就会触发一次data事件,全部读取(或写入)完毕,触发end事件。如果发生错误,则触发error事件。
一个对象只要部署了数据流接口,就可以从它读取数据,或者写入数据。Node内部很多涉及IO处理的对象,都部署了Stream接口,例如:文件读写、HTTP 请求的读写、TCP 连接、标准输入输出。 - 从流中读取数据
fs.createReadStream方法就是以”数据流“的方式读取文件,这可以在文件还没有读取完的情况下,就输出到标准输出。这显然对大文件的读取非常有利。
var fs = require("fs");
var data = '';
// 创建可读流
var readerStream = fs.createReadStream('input.txt');
// 设置编码为 utf8。
readerStream.setEncoding('UTF8');
// 处理流事件 --> data, end, and error
readerStream.on('data', function(chunk) {
data += chunk;
});
readerStream.on('end',function(){
console.log(data);
});
- 写入流
var writerStream = fs.createWriteStream('output.txt');
// 使用 utf8 编码写入数据
writerStream.write('hello','UTF8');
// 表示不再将数据写入Writable。
writerStream.end();
// 处理流事件 --> data, end, and error
writerStream.on('finish', function() {
console.log("写入完成。");
});
- 管道流
管道提供了一个输出流到输入流的机制,通常我们用于从一个流中获取数据并将数据传递到另外一个流中。
如上面的图片所示,我们把文件比作装水的桶,而水就是文件里的内容,我们用一根管子(pipe)连接两个桶使得水从一个桶流入另一个桶,这样就慢慢的实现了大文件的复制过程。
语法:readable.pipe(destination[, options])该readable.pipe()方法将Writable流附加到readable。
// 创建一个可读流
var readerStream = fs.createReadStream('input.txt');
// 创建一个可写流
var writerStream = fs.createWriteStream('output.txt');
// 管道读写操作
// 读取 input.txt 文件内容,并将内容写入到 output.txt 文件中
readerStream.pipe(writerStream);
- 链式流
var fs = require("fs");
var zlib = require('zlib');
// 压缩 input.txt 文件为 input.txt.gz
fs.createReadStream('input.txt')
.pipe(zlib.createGzip())
.pipe(fs.createWriteStream('input.txt.gz'));
五、全局对象
Node.js 中的全局对象是 global,所有全局变量(除了 global 本身以外)都是 global 对象的属性。
- __filename
__filename 表示当前正在执行的脚本的文件名。它将输出文件所在位置的绝对路径,且和命令行参数所指定的文件名不一定相同。如果在模块中,返回的值是模块文件的路径。 - __dirname
__dirname 表示当前执行脚本所在的目录。 - process
用于描述当前Node.js 进程状态的对象,提供了一个与操作系统的简单接口。通常在你写本地命令行程序的时候,少不了要 和它打交道。
六、fs模块
- readFile(),readFileSync()
var fs = require("fs")
//异步读取
fs.readFile('input.txt', function (err, data) {
if (err) {
return console.error(err);
}
console.log("异步读取: " + data.toString());
});
// 同步读取
var data = fs.readFileSync('input.txt');
- writeFile(),writeFileSync()
//异步 在回调函数之前可以加编码
fs.writeFile('message.txt', 'Hello Node.js', (err) => {
if (err) throw err;
console.log('It\'s saved!');
});
//同步
fs.writeFileSync(fileName, str, 'utf8');
- mkdir()
mkdir方法用于新建目录。mkdir接受三个参数,第一个是目录名,第二个是权限值,第三个是回调函数。
var fs = require('fs');
fs.mkdir('./helloDir',0777, function (err) {
if (err) throw err;
});
- readdir()
eaddir方法用于读取目录,返回一个所包含的文件和子目录的数组。 - 打开文件
以下为在异步模式下打开文件的语法格式:fs.open(path, flags[, mode], callback),flags - 文件打开的行为。
var fs = require("fs");
// 异步打开文件
console.log("准备打开文件!");
fs.open('input.txt', 'r+', function(err, fd) {
if (err) {
return console.error(err);
}
console.log("文件打开成功!");
});
- stat()
fs.stat(path, callback),callback - 回调函数,带有两个参数如:(err, stats), stats 是 fs.Stats 对象。
var fs = require('fs');
fs.stat('/Users/liuht/code/itbilu/demo/fs.js', function (err, stats) {
console.log(stats.isFile()); //true
})
- watchfile(),unwatchfile()
watchfile方法监听一个文件,如果该文件发生变化,就会自动触发回调函数。
var fs = require('fs');
fs.watchFile('./testFile.txt', function (curr, prev) {
console.log('the current mtime is: ' + curr.mtime);
console.log('the previous mtime was: ' + prev.mtime);
});
fs.writeFile('./testFile.txt', "changed", function (err) {
if (err) throw err;
console.log("file write complete");
});
unwatchfile方法用于解除对文件的监听。
- createReadStream()
createReadStream方法往往用于打开大型的文本文件,创建一个读取操作的数据流。所谓大型文本文件,指的是文本文件的体积很大,读取操作的缓存装不下,只能分成几次发送,每次发送会触发一个data事件,发送结束会触发end事件。(具体代码见四、Stream(流)) - createWriteStream()
createWriteStream方法创建一个写入数据流对象,该对象的write方法用于写入数据,end方法用于结束写入操作。(具体代码见四、Stream(流)) createWriteStream方法和createReadStream方法配合,可以实现拷贝大型文件。
function fileCopy(filename1, filename2, done) {
var input = fs.createReadStream(filename1);
var output = fs.createWriteStream(filename2);
input.on('data', function(d) { output.write(d); });
input.on('error', function(err) { throw err; });
input.on('end', function() {
output.end();
if (done) done();
});
}
七、Web 模块
1.基本用法
1.1 处理get请求
var http = require('http');
var fs = require('fs');
//调用http模块的createServer方法,创造一个服务器实例
http.createServer(function (request, response){
<!--方式一-->
fs.readFile('data.txt', function readData(err, data) {
response.writeHead(200, {'Content-Type': 'text/plain'});
response.end(data);
});
<!--方式二-->
fs.createReadStream(`${__dirname}/index.html`).pipe(response);
}).listen(8080, '127.0.0.1');
response.end方法用来写入HTTP回应的具体内容,以及回应完成后关闭本次对话。
1.2request对象
createServer方法的回调函数的第一个参数是一个request对象,拥有属性:url:发出请求的网址;method:HTTP请求的方法;headers:HTTP请求的所有HTTP头信息。
- 获取请求的路径名
var url = require('url');
var pathname = url.parse(request.url).pathname;
- setEncoding()方法用于设置请求的编码。
request.setEncoding("utf8");
1.3 处理post请求
//例如表单提交post请求
var http = require('http');
http.createServer(function (req, res) {
var content = "";
req.on('data', function (chunk) {
content += chunk;
});
req.on('end', function () {
res.writeHead(200, {"Content-Type": "text/plain"});
res.write("You've sent: " + content);
res.end();
});
}).listen(8080);
data事件会在数据接收过程中,每收到一段数据就触发一次,接收到的数据被传入回调函数。end事件则是在所有数据接收完成后触发。 对上面代码稍加修改,就可以做出文件上传的功能。
http.createServer(function (request, response) {
response.writeHead(200);
file = fs.createWriteStream("destination.md");
request.pipe(file);
fileSize = request.headers['content-length'];
uploadedBytes = 0;
request.on('data', function (d) {
uploadedBytes += d.length;
var p = (uploadedBytes / fileSize) * 100;
response.write("Uploading " + parseInt(p, 0) + " %\n");
});
request.on('end', function () {
response.end("File Upload Complete");
});
}).listen(3030, function () {
console.log("server started");
});
2.发出请求
2.1 get()
function getMsg(callback) {
return http.get({
host: 'personatestuser.org',
path: '/email'
}, function(response) {
var data = '';
response.on('data', function(d) {
data += d;
});
response.on('end', function() {
var parsed = JSON.parse(data);
callback({
email: parsed.email,
password: parsed.pass
});
});
});
},
2.2 post()
equest方法用于发出HTTP请求,它的使用格式:http.request(options[, callback]). 下面是发送POST请求的一个例子。
const postData = querystring.stringify({
'msg': '你好世界'
});
const options = {
hostname: 'nodejs.cn',
port: 80,
path: '/upload',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
};
const req = http.request(options, (res) => {
console.log(`状态码: ${res.statusCode}`);
console.log(`响应头: ${JSON.stringify(res.headers)}`);
res.setEncoding('utf8');
res.on('data', (chunk) => {
console.log(`响应主体: ${chunk}`);
});
res.on('end', () => {
console.log('响应中已无数据');
});
});
req.on('error', (e) => {
console.error(`请求遇到问题: ${e.message}`);
});
// 将数据写入请求主体。
req.write(postData);
req.end();