NodeJS学习

一、安装Node.js(CentOS7)

echo "downloan node"
wget https://nodejs.org/dist/v10.9.0/node-v10.9.0-linux-x64.tar.xz
echo "downloan node success"    
tar xf  node-v10.9.0-linux-x64.tar.xz
echo "node version:"       
cd node-v10.9.0-linux-x64/                  
./bin/node -v           

二、第一个Node.js程序

在Node解压包下的bin文件夹下创建一个HelloWorld.js文件:

 console.log("Hello World"); 

然后输入以下指令:

./node helloword.js

输出:

helloworld

交互模式下:

$ node
> console.log('Hello World!');
Hello World!

三、Node.js 创建第一个应用:

//http 请求
var http = require('http');

//创建服务器(实际上是发送http报文至浏览器)
http.createServer(function (request, response) {

    // 发送 HTTP 头部 
    // HTTP 状态值: 200 : OK
    // 内容类型: text/plain
    response.writeHead(200, {'Content-Type': 'text/plain'});

    // 响应发送响应数据 "Hello World"
    response.end('Hello World\n');
}).listen(8888);

// 终端打印如下信息
console.log('Server running at http://127.0.0.1:8888/');

四、npm使用:

检查npm:

./node npm -v

本地安装与全局安装:

./node npm install express      # 本地安装
./node npm install express -g   # 全局安装

本地安装

    1. 将安装包放在 ./node_modules 下(运行 npm 命令时所在的目录),如果没有 node_modules 目录,会在当前执行 npm 命令的目录下生成 node_modules 目录。
    2. 可以通过 require() 来引入本地安装的包。 

全局安装

    1. 将安装包放在 /usr/local 下或者你 node 的安装目录。
    2. 可以直接在命令行里使用。

安装异常解决方案:

如出现以下异常:

npm err! Error: connect ECONNREFUSED 127.0.0.1:8087 


解决方法为:

./node npm config set proxy null

拆卸模块:

./node npm uninstall express

更新模块:

./node npm update express

搜索模块:

./node npm search express

查看模块列表:

./node npm ls

创建模块:

# 创建JSON文件
$ ./node npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (node_modules) runoob                   # 模块名
version: (1.0.0) 
description: Node.js 测试模块(www.runoob.com)  # 描述
entry point: (index.js) 
test command: make test
git repository: https://github.com/runoob/runoob.git  # Github 地址
keywords: 
author: 
license: (ISC) 
About to write to ……/node_modules/package.json:      # 生成地址

{
  "name": "runoob",
  "version": "1.0.0",
  "description": "Node.js 测试模块(www.runoob.com)",
  ……
}


Is this ok? (yes) yes


# 填写邮箱地址
$ ./node npm adduser
Username: mcmohd
Password:
Email: (this IS public) mcmohd@gmail.com

# 发布模块
$ ./node npm publish

五、交互式编程:

#直接进入
./node 

ctrl+D退出

六、回调函数:(非阻塞式函数,优化读写性能)

格式:

//callback 回调函数作为最后一个参数,优化性能
function foo1(name, age, callback) { }
function foo2(value, callback1, callback2) { }

实例:

var fs = require("fs");

fs.readFile('input.txt', function (err, data) {
    if (err) return console.error(err);
    console.log(data.toString());
});

console.log("程序执行结束!");

七、事件驱动模型(响应式编程):

// 引入 events 模块
// 执行顺序1
var events = require('events');
// 创建 eventEmitter 对象
// 执行顺序2
var eventEmitter = new events.EventEmitter();
 
// 创建事件处理程序
// 执行顺序5
var connectHandler = function connected() {
   console.log('连接成功。');
  
   // 触发 data_received 事件 
   eventEmitter.emit('data_received');
}
 
// 绑定 connection 事件处理程序
// 执行顺序3
eventEmitter.on('connection', connectHandler);
 
// 使用匿名函数绑定 data_received 事件
// 执行顺序6
eventEmitter.on('data_received', function(){
   console.log('数据接收成功。');
});
 
// 触发 connection 事件
// 执行顺序4 
eventEmitter.emit('connection');

// 执行顺序7
console.log("程序执行完毕。");

执行结果:

连接成功。
数据接收成功。
程序执行完毕。

八、EventEmitter:

(1)使用原则:

优先使用EventEmitter的子类包括 fs、net、 http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。

(2)使用方法:

// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();

// 绑定触发对象
event.on('触发对象名', function() { 
    console.log('some_event 事件触发'); 
}); 

// 触发事件对象
event.emit('触发对象名'); 

(3)方法:

序号方法 & 描述
1addListener(event, listener)
为指定事件添加一个监听器到监听器数组的尾部。
2on(event, listener)
为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数。
server.on('connection', function (stream) {
  console.log('someone connected!');
});
3once(event, listener)
为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器。
server.once('connection', function (stream) {
  console.log('Ah, we have our first user!');
});
4removeListener(event, listener)

移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器。

它接受两个参数,第一个是事件名称,第二个是回调函数名称。

var callback = function(stream) {
  console.log('someone connected!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);
5removeAllListeners([event])
移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器。
6setMaxListeners(n)
默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于提高监听器的默认限制的数量。
7listeners(event)
返回指定事件的监听器数组。
8emit(event, [arg1], [arg2], [...])
按监听器的顺序执行执行每个监听器,如果事件有注册监听返回 true,否则返回 false。

类方法:

序号方法 & 描述
1listenerCount(emitter, event)
返回指定事件的监听器数量。
events.EventEmitter.listenerCount(emitter, eventName) //已废弃,不推荐
events.emitter.listenerCount(eventName) //推荐

事件:

序号事件 & 描述
1newListener
  • event - 字符串,事件名称

  • listener - 处理事件函数

该事件在添加新监听器时被触发。

2removeListener
  • event - 字符串,事件名称

  • listener - 处理事件函数

从指定监听器数组中删除一个监听器。需要注意的是,此操作将会改变处于被删监听器之后的那些监听器的索引。

九、Buffer

(1)作用:

处理诸如TCP/IP或文件等二进制对象,完善JS中只能处理字符串类型的短板。

在v6.0之前创建Buffer对象直接使用new Buffer()构造函数来创建对象实例,但是Buffer对内存的权限操作相比很大,可以直接捕获一些敏感信息,所以在v6.0以后,官方文档里面建议使用 Buffer.from() 接口去创建Buffer对象。 

(2)buffer与字符编码:

const buf = Buffer.from('buoy2019', 'ascii');

// 输出 62756f7932303139
console.log(buf.toString('hex'));

// 输出 YnVveTIwMTk=
console.log(buf.toString('base64'));

支持的字符编码:

ascii - 仅支持 7 位 ASCII 数据。如果设置去掉高位的话,这种编码是非常快的。

utf8 - 多字节编码的 Unicode 字符。许多网页和其他文档格式都使用 UTF-8 。

utf16le - 2 或 4 个字节,小字节序编码的 Unicode 字符。支持代理对(U+10000 至 U+10FFFF)。

ucs2 - utf16le 的别名。

base64 - Base64 编码。

latin1 - 一种把 Buffer 编码成一字节编码的字符串的方式。

binary - latin1 的别名。

hex - 将每个字节编码为两个十六进制字符。

(3)创建buffer类:

创建api:

Buffer.alloc(size[, fill[, encoding]]): 返回一个指定大小的 Buffer 实例,如果没有设置 fill,则默认填满 0
    
Buffer.allocUnsafe(size): 返回一个指定大小的 Buffer 实例,但是它不会被初始化,所以它可能包含敏感的数据
    
Buffer.allocUnsafeSlow(size)
    
Buffer.from(array): 返回一个被 array 的值初始化的新的 Buffer 实例(传入的 array 的元素只能是数字,不然就会自动被 0 覆盖)
    
Buffer.from(arrayBuffer[, byteOffset[, length]]): 返回一个新建的与给定的 ArrayBuffer 共享同一内存的 Buffer。
    
Buffer.from(buffer): 复制传入的 Buffer 实例的数据,并返回一个新的 Buffer 实例
    
Buffer.from(string[, encoding]): 返回一个被 string 的值初始化的新的 Buffer 实例

实例:

//创建一个长度为 10、且用 0 填充的 Buffer。
const buf1 = Buffer.alloc(10);

// 创建一个长度为 10、且用 0x1 填充的 Buffer。 
const buf2 = Buffer.alloc(10, 1);

// 创建一个长度为 10、且未初始化的 Buffer。
// 这个方法比调用 Buffer.alloc() 更快,
// 但返回的 Buffer 实例可能包含旧数据,
// 因此需要使用 fill() 或 write() 重写。
const buf3 = Buffer.allocUnsafe(10);

// 创建一个包含 [0x1, 0x2, 0x3] 的 Buffer。
const buf4 = Buffer.from([1, 2, 3]);
console.log(buf4.toString());

// 创建一个包含 UTF-8 字节 [0x74, 0xc3, 0xa9, 0x73, 0x74] 的 Buffer。
const buf5 = Buffer.from('tést');
console.log(buf5.toString());

// 创建一个包含 Latin-1 字节 [0x74, 0xe9, 0x73, 0x74] 的 Buffer。
const buf6 = Buffer.from('tést', 'latin1');
console.log(buf6.toString());

(4)写入缓冲区:

buf.write(string[, offset[, length]][, encoding])

参数

参数描述如下:

string - 写入缓冲区的字符串。

offset - 缓冲区开始写入的索引值,默认为 0 。

length - 写入的字节数,默认为 buffer.length

encoding - 使用的编码。默认为 'utf8' 。

根据 encoding 的字符编码写入 string 到 buf 中的 offset 位置。 length 参数是写入的字节数。 如果 buf 没有足够的空间保存整个字符串,则只会写入 string 的一部分。 只部分解码的字符不会被写入。

返回值:

返回实际写入的大小。如果 buffer 空间不足, 则只会写入部分字符串。

实例:

buf = Buffer.alloc(256);
len = buf.write("www.baidu.com");

console.log("写入字节数 : "+  len);

输出:

../node mybuffer03.js
写入字节数 : 13

(5)从缓冲区读取数据:

语法:

buf.toString([encoding[, start[, end]]])


参数描述如下:

encoding - 使用的编码。默认为 'utf8' 。

start - 指定开始读取的索引位置,默认为 0。

end - 结束位置,默认为缓冲区的末尾。

实例:

buf = Buffer.alloc(26);
for (var i = 0 ; i < 26 ; i++) {
  buf[i] = i + 97;
}

console.log( buf.toString('ascii'));       // 输出: abcdefghijklmnopqrstuvwxyz
console.log( buf.toString('ascii',0,5));   //使用 'ascii' 编码, 并输出: abcde
console.log( buf.toString('utf8',0,5));    // 使用 'utf8' 编码, 并输出: abcde
console.log( buf.toString(undefined,0,5)); // 使用默认的 'utf8' 编码, 并输出: abcde

(6)buffer转换为json对象:

实例:

const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]);
const json = JSON.stringify(buf);

// 输出: {"type":"Buffer","data":[1,2,3,4,5]}
console.log(json);

const copy = JSON.parse(json, (key, value) => {
  return value && value.type === 'Buffer' ?
    Buffer.from(value.data) :
    value;
});

// 输出: <Buffer 01 02 03 04 05>
console.log(copy);

输出:

{"type":"Buffer","data":[1,2,3,4,5]}
<Buffer 01 02 03 04 05>

(7)缓冲区合并:

语法:

Buffer.concat(list[, totalLength])


list - 用于合并的 Buffer 对象数组列表。

totalLength - 指定合并后Buffer对象的总长度。

实例:

var buffer1 = Buffer.from(('百度'));
var buffer2 = Buffer.from(('www.baidu.com'));
var buffer3 = Buffer.concat([buffer1,buffer2]);
console.log("buffer3 内容: " + buffer3.toString());

输出:

buffer3 内容: 百度www.baidu.com

(8)缓冲区比较:

语法:

buf.compare(otherBuffer);

参数:
otherBuffer - 与 buf 对象比较的另外一个 Buffer 对象。 

返回值:

返回一个数字,表示 buf 在 otherBuffer 之前,之后或相同。

实例:

var buffer1 = Buffer.from('ABC');
var buffer2 = Buffer.from('ABCD');
var result = buffer1.compare(buffer2);

if(result < 0) {
   console.log(buffer1 + " 在 " + buffer2 + "之前");
}else if(result == 0){
   console.log(buffer1 + " 与 " + buffer2 + "相同");
}else {
   console.log(buffer1 + " 在 " + buffer2 + "之后");
}

输出:

ABC在ABCD之前

(9)拷贝缓冲区:

语法:

buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]])

targetBuffer - 要拷贝的 Buffer 对象。

targetStart - 数字, 可选, 默认: 0

sourceStart - 数字, 可选, 默认: 0

sourceEnd - 数字, 可选, 默认: buffer.length

实例:

var buf1 = Buffer.from('abcdefghijkl');
var buf2 = Buffer.from('RUNOOB');

//将 buf2 插入到 buf1 指定位置上
buf2.copy(buf1, 2);

console.log(buf1.toString());

(10)缓冲区裁剪:

语法:

buf.slice([start[, end]])

参数:
start - 数字, 可选, 默认: 0
end - 数字, 可选, 默认: buffer.length

返回值:
返回一个新的缓冲区,它和旧缓冲区指向同一块内存,但是从索引 start 到 end 的位置剪切。

实例:

var buffer1 = Buffer.from('baidu');
// 剪切缓冲区
var buffer2 = buffer1.slice(0,2);
console.log("buffer2 content: " + buffer2.toString());

输出:

buffer2 content: ba

(11)缓冲区长度:

语法:

buf.length;

返回值:
返回 Buffer 对象所占据的内存长度。

实例:

var buffer = Buffer.from('www.baidu.com');
//  缓冲区长度
console.log("buffer length: " + buffer.length);

输出:

buffer length: 13

(12)方法列表:

序号方法 & 描述
1new Buffer(size)
分配一个新的 size 大小单位为8位字节的 buffer。 注意, size 必须小于 kMaxLength,否则,将会抛出异常 RangeError。废弃的: 使用 Buffer.alloc() 代替(或 Buffer.allocUnsafe())。
2new Buffer(buffer)
拷贝参数 buffer 的数据到 Buffer 实例。废弃的: 使用 Buffer.from(buffer) 代替。
3new Buffer(str[, encoding])
分配一个新的 buffer ,其中包含着传入的 str 字符串。 encoding 编码方式默认为 'utf8'。 废弃的: 使用 Buffer.from(string[, encoding]) 代替。
4buf.length
返回这个 buffer 的 bytes 数。注意这未必是 buffer 里面内容的大小。length 是 buffer 对象所分配的内存数,它不会随着这个 buffer 对象内容的改变而改变。
5buf.write(string[, offset[, length]][, encoding])
根据参数 offset 偏移量和指定的 encoding 编码方式,将参数 string 数据写入buffer。 offset 偏移量默认值是 0, encoding 编码方式默认是 utf8。 length 长度是将要写入的字符串的 bytes 大小。 返回 number 类型,表示写入了多少 8 位字节流。如果 buffer 没有足够的空间来放整个 string,它将只会只写入部分字符串。 length 默认是 buffer.length - offset。 这个方法不会出现写入部分字符。
6buf.writeUIntLE(value, offset, byteLength[, noAssert])
将 value 写入到 buffer 里, 它由 offset 和 byteLength 决定,最高支持 48 位无符号整数,小端对齐,例如:
const buf = Buffer.allocUnsafe(6);

buf.writeUIntLE(0x1234567890ab, 0, 6);

// 输出: <Buffer ab 90 78 56 34 12>
console.log(buf);
noAssert 值为 true 时,不再验证 value 和 offset 的有效性。 默认是 false。
7buf.writeUIntBE(value, offset, byteLength[, noAssert])
将 value 写入到 buffer 里, 它由 offset 和 byteLength 决定,最高支持 48 位无符号整数,大端对齐。noAssert 值为 true 时,不再验证 value 和 offset 的有效性。 默认是 false。
const buf = Buffer.allocUnsafe(6);

buf.writeUIntBE(0x1234567890ab, 0, 6);

// 输出: <Buffer 12 34 56 78 90 ab>
console.log(buf);
8buf.writeIntLE(value, offset, byteLength[, noAssert])
将value 写入到 buffer 里, 它由offset 和 byteLength 决定,最高支持48位有符号整数,小端对齐。noAssert 值为 true 时,不再验证 value 和 offset 的有效性。 默认是 false。
9buf.writeIntBE(value, offset, byteLength[, noAssert])
将value 写入到 buffer 里, 它由offset 和 byteLength 决定,最高支持48位有符号整数,大端对齐。noAssert 值为 true 时,不再验证 value 和 offset 的有效性。 默认是 false。
10buf.readUIntLE(offset, byteLength[, noAssert])
支持读取 48 位以下的无符号数字,小端对齐。noAssert 值为 true 时, offset 不再验证是否超过 buffer 的长度,默认为 false。
11buf.readUIntBE(offset, byteLength[, noAssert])
支持读取 48 位以下的无符号数字,大端对齐。noAssert 值为 true 时, offset 不再验证是否超过 buffer 的长度,默认为 false。
12buf.readIntLE(offset, byteLength[, noAssert])
支持读取 48 位以下的有符号数字,小端对齐。noAssert 值为 true 时, offset 不再验证是否超过 buffer 的长度,默认为 false。
13buf.readIntBE(offset, byteLength[, noAssert])
支持读取 48 位以下的有符号数字,大端对齐。noAssert 值为 true 时, offset 不再验证是否超过 buffer 的长度,默认为 false。
14buf.toString([encoding[, start[, end]]])
根据 encoding 参数(默认是 'utf8')返回一个解码过的 string 类型。还会根据传入的参数 start (默认是 0) 和 end (默认是 buffer.length)作为取值范围。
15buf.toJSON()
将 Buffer 实例转换为 JSON 对象。
16buf[index]
获取或设置指定的字节。返回值代表一个字节,所以返回值的合法范围是十六进制0x00到0xFF 或者十进制0至 255。
17buf.equals(otherBuffer)
比较两个缓冲区是否相等,如果是返回 true,否则返回 false。
18buf.compare(otherBuffer)
比较两个 Buffer 对象,返回一个数字,表示 buf 在 otherBuffer 之前,之后或相同。
19buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]])
buffer 拷贝,源和目标可以相同。 targetStart 目标开始偏移和 sourceStart 源开始偏移默认都是 0。 sourceEnd 源结束位置偏移默认是源的长度 buffer.length 。
20buf.slice([start[, end]])
剪切 Buffer 对象,根据 start(默认是 0 ) 和 end (默认是 buffer.length ) 偏移和裁剪了索引。 负的索引是从 buffer 尾部开始计算的。
21buf.readUInt8(offset[, noAssert])
根据指定的偏移量,读取一个无符号 8 位整数。若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 如果这样 offset 可能会超出buffer 的末尾。默认是 false。
22buf.readUInt16LE(offset[, noAssert])
根据指定的偏移量,使用特殊的 endian 字节序格式读取一个无符号 16 位整数。若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。
23buf.readUInt16BE(offset[, noAssert])
根据指定的偏移量,使用特殊的 endian 字节序格式读取一个无符号 16 位整数,大端对齐。若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。
24buf.readUInt32LE(offset[, noAssert])
根据指定的偏移量,使用指定的 endian 字节序格式读取一个无符号 32 位整数,小端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer 的末尾。默认是 false。
25buf.readUInt32BE(offset[, noAssert])
根据指定的偏移量,使用指定的 endian 字节序格式读取一个无符号 32 位整数,大端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer 的末尾。默认是 false。
26buf.readInt8(offset[, noAssert])
根据指定的偏移量,读取一个有符号 8 位整数。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。
27buf.readInt16LE(offset[, noAssert])
根据指定的偏移量,使用特殊的 endian 格式读取一个 有符号 16 位整数,小端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。
28buf.readInt16BE(offset[, noAssert])
根据指定的偏移量,使用特殊的 endian 格式读取一个 有符号 16 位整数,大端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。
29buf.readInt32LE(offset[, noAssert])
根据指定的偏移量,使用指定的 endian 字节序格式读取一个有符号 32 位整数,小端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer 的末尾。默认是 false。
30buf.readInt32BE(offset[, noAssert])
根据指定的偏移量,使用指定的 endian 字节序格式读取一个有符号 32 位整数,大端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer 的末尾。默认是 false。
31buf.readFloatLE(offset[, noAssert])
根据指定的偏移量,使用指定的 endian 字节序格式读取一个 32 位双浮点数,小端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer的末尾。默认是 false。
32buf.readFloatBE(offset[, noAssert])
根据指定的偏移量,使用指定的 endian 字节序格式读取一个 32 位双浮点数,大端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer的末尾。默认是 false。
33buf.readDoubleLE(offset[, noAssert])
根据指定的偏移量,使用指定的 endian字节序格式读取一个 64 位双精度数,小端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer 的末尾。默认是 false。
34buf.readDoubleBE(offset[, noAssert])
根据指定的偏移量,使用指定的 endian字节序格式读取一个 64 位双精度数,大端对齐。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer 的末尾。默认是 false。
35buf.writeUInt8(value, offset[, noAssert])
根据传入的 offset 偏移量将 value 写入 buffer。注意:value 必须是一个合法的无符号 8 位整数。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则不要使用。默认是 false。
36buf.writeUInt16LE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的无符号 16 位整数,小端对齐。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出buffer的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。
37buf.writeUInt16BE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的无符号 16 位整数,大端对齐。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出buffer的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。
38buf.writeUInt32LE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式(LITTLE-ENDIAN:小字节序)将 value 写入buffer。注意:value 必须是一个合法的无符号 32 位整数,小端对齐。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着value 可能过大,或者offset可能会超出buffer的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。
39buf.writeUInt32BE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式(Big-Endian:大字节序)将 value 写入buffer。注意:value 必须是一个合法的有符号 32 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者offset可能会超出buffer的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。
40buf.writeInt8(value, offset[, noAssert])
41buf.writeInt16LE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的 signed 16 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false 。
42buf.writeInt16BE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的 signed 16 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false 。
43buf.writeInt32LE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的 signed 32 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。
44buf.writeInt32BE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的 signed 32 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。
45buf.writeFloatLE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer 。注意:当 value 不是一个 32 位浮点数类型的值时,结果将是不确定的。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。
46buf.writeFloatBE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer 。注意:当 value 不是一个 32 位浮点数类型的值时,结果将是不确定的。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。
47buf.writeDoubleLE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个有效的 64 位double 类型的值。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成value被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。
48buf.writeDoubleBE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个有效的 64 位double 类型的值。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成value被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。
49buf.fill(value[, offset][, end])
使用指定的 value 来填充这个 buffer。如果没有指定 offset (默认是 0) 并且 end (默认是 buffer.length) ,将会填充整个buffer。

 十、Node.js Stream(流):

定义:

Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对http 服务器发起请求的request 对象就是一个 Stream,还有stdout(标准输出)。

Node.js,Stream 有四种流类型:

    Readable - 可读操作。

    Writable - 可写操作。

    Duplex - 可读可写操作.

    Transform - 操作被写入数据,然后读出结果。

所有的 Stream 对象都是 EventEmitter 的实例。常用的事件有:

    data - 当有数据可读时触发。

    end - 没有更多的数据可读时触发。

    error - 在接收和写入过程中发生错误时触发。

    finish - 所有数据已被写入到底层系统时触发。

(2)从流中读取数据:

实例:

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);
});

readerStream.on('error', function(err){
   console.log(err.stack);
});

console.log("程序执行完毕");

输出:

程序执行完毕
the input text !!

(3)写入流:

实例:

var fs = require("fs");
var data = '百度搜索地址:www.baidu.com';

// 创建一个可以写入的流,写入到文件 output.txt 中
var writerStream = fs.createWriteStream('output.txt');

// 使用 utf8 编码写入数据
writerStream.write(data,'UTF8');

// 标记文件末尾
writerStream.end();

// 处理流事件 --> data, end, and error
writerStream.on('finish', function() {
    console.log("写入完成。");
});

writerStream.on('error', function(err){
   console.log(err.stack);
});

console.log("程序执行完毕");

(4)管道流(管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中。):

var fs = require("fs");

// 创建一个可读流
var readerStream = fs.createReadStream('input.txt');

// 创建一个可写流
var writerStream = fs.createWriteStream('output.txt');

// 管道读写操作
// 读取 input.txt 文件内容,并将内容写入到 output.txt 文件中
readerStream.pipe(writerStream);

console.log("程序执行完毕");

(5)链式流(连接输出流到另外一个流并创建多个流操作链的机制。链式流一般用于管道操作。):

(压缩)实例:

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'));
  
console.log("文件压缩完成。");

(解压)实例:

var fs = require("fs");
var zlib = require('zlib');

// 解压 input.txt.gz 文件为 input.txt
fs.createReadStream('input.txt.gz')
  .pipe(zlib.createGunzip())
  .pipe(fs.createWriteStream('input.txt'));
  
console.log("文件解压完成。");

十一、Node.js模块系统:

(1)作用:

为了让Node.js的文件可以相互调用

(2)实例:

创建模块

/**
require('./hello') 引入了当前目录下的 hello.js 文件(./ 为当前目录,node.js 默认后缀为 js)。

Node.js 提供了 exports 和 require 两个对象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口,即所获取模块的 exports 对象。 
*/


//main.js 
var Hello = require('./hello'); 
hello = new Hello(); 
hello.setName('BYVoid'); 
hello.sayHello(); 


//hello.js 
function Hello() { 
    var name; 
    this.setName = function(thyName) { 
        name = thyName; 
    }; 
    this.sayHello = function() { 
        console.log('Hello ' + name); 
    }; 
}; 
module.exports = Hello;

(3)exports 和 module.exports 的使用:

如果要对外暴露属性或方法,就用 exports 就行,要暴露对象(类似class,包含了很多属性和方法),就用 module.exports。

(4)调用原理:

十二、node路由:

(1)执行顺序:

执行index.js 编译执行 server.js与router.js

(2)实例:

server.js


// 绑定http事件
var http = require("http");
// 绑定url事件
var url = require("url");

//创建模块start ,参数为route.js模块route 
function start(route) {
  function onRequest(request, response) {
    //解析url路径
    var pathname = url.parse(request.url).pathname;
    console.log("Request for " + pathname + " received.");
 
    //调用route.js中的route模块
    route(pathname);
 
    //编辑响应模式为http报文
    response.writeHead(200, {"Content-Type": "text/plain"});
    response.write("Hello World");
    response.end();
  }

  //监听端口为8888 
  http.createServer(onRequest).listen(8888);
  console.log("Server has started.");
}

//暴露模块 
exports.start = start;

router.js:

//定义模块route
function route(pathname) {
  console.log("About to route a request for " + pathname);
}

//暴露模块 
exports.route = route;

index.js:

// 映射为server.js与router.js
var server = require("./server");
var router = require("./router");

// 启动server.js的start模块
server.start(router.route);

输出:

十三、全局对象与全局变量:

(1)全局对象:

node.js的全局对象为global

(2)全局变量使用原则:

注意: 最好不要使用 var 定义变量以避免引入全局变量,因为全局变量会污染命名空间,提高代码的耦合风险。

(3)全局变量定义:

在最外层定义的变量;
全局对象的属性;
隐式定义的变量(未定义直接赋值的变量)。

(4)返回路径名:

// 输出全局变量 __filename 的值
console.log( __filename );

(5)返回所在目录:

// 输出全局变量 __dirname 的值
console.log( __dirname );

(6)定时器:

function printHello(){
   console.log( "Hello, World!");
}
// 两秒后执行以上函数
setTimeout(printHello, 2000);

(7)清除定时器:

function printHello(){
   console.log( "Hello, World!");
}
// 两秒后执行以上函数
var t = setTimeout(printHello, 2000);

// 清除定时器
clearTimeout(t);

(8)全局定时器:

function printHello(){
   console.log( "Hello, World!");
}
// 两秒后执行以上函数
setInterval(printHello, 2000);

//setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭。

(9)console 方法:

序号方法 & 描述
1console.log([data][, ...])
向标准输出流打印字符并以换行符结束。该方法接收若干 个参数,如果只有一个参数,则输出这个参数的字符串形式。如果有多个参数,则 以类似于C 语言 printf() 命令的格式输出。
2console.info([data][, ...])
该命令的作用是返回信息性消息,这个命令与console.log差别并不大,除了在chrome中只会输出文字外,其余的会显示一个蓝色的惊叹号。
3console.error([data][, ...])
输出错误消息的。控制台在出现错误时会显示是红色的叉子。
4console.warn([data][, ...])
输出警告消息。控制台出现有黄色的惊叹号。
5console.dir(obj[, options])
用来对一个对象进行检查(inspect),并以易于阅读和打印的格式显示。
6console.time(label)
输出时间,表示计时开始。
7console.timeEnd(label)
结束时间,表示计时结束。
8console.trace(message[, ...])
当前执行的代码在堆栈中的调用路径,这个测试函数运行很有帮助,只要给想测试的函数里面加入 console.trace 就行了。
9console.assert(value[, message][, ...])
用于判断某个表达式或变量是否为真,接收两个参数,第一个参数是表达式,第二个参数是字符串。只有当第一个参数为false,才会输出第二个参数,否则不会有任何结果。

(10)process:

1、定义:

 process 是一个全局变量,即 global 对象的属性。

2、方法:

序号事件 & 描述
1exit
当进程准备退出时触发。
2beforeExit
当 node 清空事件循环,并且没有其他安排时触发这个事件。通常来说,当没有进程安排时 node 退出,但是 'beforeExit' 的监听器可以异步调用,这样 node 就会继续执行。
3uncaughtException
当一个异常冒泡回到事件循环,触发这个事件。如果给异常添加了监视器,默认的操作(打印堆栈跟踪信息并退出)就不会发生。
4Signal 事件
当进程接收到信号时就触发。信号列表详见标准的 POSIX 信号名,如 SIGINT、SIGUSR1 等。

3、实例:

process.on('exit', function(code) {

  // 以下代码永远不会执行
  setTimeout(function() {
    console.log("该代码不会执行");
  }, 0);
  
  console.log('退出码为:', code);
});
console.log("程序执行结束");

4、退出状态码

退出状态码如下所示:

状态码名称 & 描述
1Uncaught Fatal Exception
有未捕获异常,并且没有被域或 uncaughtException 处理函数处理。
2Unused
保留
3Internal JavaScript Parse Error
JavaScript的源码启动 Node 进程时引起解析错误。非常罕见,仅会在开发 Node 时才会有。
4Internal JavaScript Evaluation Failure
JavaScript 的源码启动 Node 进程,评估时返回函数失败。非常罕见,仅会在开发 Node 时才会有。
5Fatal Error
V8 里致命的不可恢复的错误。通常会打印到 stderr ,内容为: FATAL ERROR
6Non-function Internal Exception Handler
未捕获异常,内部异常处理函数不知为何设置为on-function,并且不能被调用。
7Internal Exception Handler Run-Time Failure
未捕获的异常, 并且异常处理函数处理时自己抛出了异常。例如,如果 process.on('uncaughtException') 或 domain.on('error') 抛出了异常。
8Unused
保留
9Invalid Argument
可能是给了未知的参数,或者给的参数没有值。
10Internal JavaScript Run-Time Failure
JavaScript的源码启动 Node 进程时抛出错误,非常罕见,仅会在开发 Node 时才会有。
12Invalid Debug Argument
设置了参数--debug 和/或 --debug-brk,但是选择了错误端口。
128Signal Exits
如果 Node 接收到致命信号,比如SIGKILL 或 SIGHUP,那么退出代码就是128 加信号代码。这是标准的 Unix 做法,退出信号代码放在高位。

5、Process 属性

Process 提供了很多有用的属性,便于我们更好的控制系统的交互:

序号.属性 & 描述
1stdout
标准输出流。
2stderr
标准错误流。
3stdin
标准输入流。
4argv
argv 属性返回一个数组,由命令行执行脚本时的各个参数组成。它的第一个成员总是node,第二个成员是脚本文件名,其余成员是脚本文件的参数。
5execPath
返回执行当前脚本的 Node 二进制文件的绝对路径。
6execArgv
返回一个数组,成员是命令行下执行脚本时,在Node可执行文件与脚本文件之间的命令行参数。
7env
返回一个对象,成员为当前 shell 的环境变量
8exitCode
进程退出时的代码,如果进程优通过 process.exit() 退出,不需要指定退出码。
9version
Node 的版本,比如v0.10.18。
10versions
一个属性,包含了 node 的版本和依赖.
11config
一个包含用来编译当前 node 执行文件的 javascript 配置选项的对象。它与运行 ./configure 脚本生成的 "config.gypi" 文件相同。
12pid
当前进程的进程号。
13title
进程名,默认值为"node",可以自定义该值。
14arch
当前 CPU 的架构:'arm'、'ia32' 或者 'x64'。
15platform
运行程序所在的平台系统 'darwin', 'freebsd', 'linux', 'sunos' 或 'win32'
16mainModule
require.main 的备选方法。不同点,如果主模块在运行时改变,require.main可能会继续返回老的模块。可以认为,这两者引用了同一个模块。

实例:

// 输出到终端
process.stdout.write("Hello World!" + "\n");

// 通过参数读取
process.argv.forEach(function(val, index, array) {
   console.log(index + ': ' + val);
});

// 获取执行路径
console.log(process.execPath);


// 平台信息
console.log(process.platform);

6、方法参考手册

Process 提供了很多有用的方法,便于我们更好的控制系统的交互:

序号方法 & 描述
1abort()
这将导致 node 触发 abort 事件。会让 node 退出并生成一个核心文件。
2chdir(directory)
改变当前工作进程的目录,如果操作失败抛出异常。
3cwd()
返回当前进程的工作目录
4exit([code])
使用指定的 code 结束进程。如果忽略,将会使用 code 0。
5getgid()
获取进程的群组标识(参见 getgid(2))。获取到得时群组的数字 id,而不是名字。
注意:这个函数仅在 POSIX 平台上可用(例如,非Windows 和 Android)。
6setgid(id)
设置进程的群组标识(参见 setgid(2))。可以接收数字 ID 或者群组名。如果指定了群组名,会阻塞等待解析为数字 ID 。
注意:这个函数仅在 POSIX 平台上可用(例如,非Windows 和 Android)。
7getuid()
获取进程的用户标识(参见 getuid(2))。这是数字的用户 id,不是用户名。
注意:这个函数仅在 POSIX 平台上可用(例如,非Windows 和 Android)。
8setuid(id)
设置进程的用户标识(参见setuid(2))。接收数字 ID或字符串名字。果指定了群组名,会阻塞等待解析为数字 ID 。
注意:这个函数仅在 POSIX 平台上可用(例如,非Windows 和 Android)。
9getgroups()
返回进程的群组 iD 数组。POSIX 系统没有保证一定有,但是 node.js 保证有。
注意:这个函数仅在 POSIX 平台上可用(例如,非Windows 和 Android)。
10setgroups(groups)
设置进程的群组 ID。这是授权操作,所以你需要有 root 权限,或者有 CAP_SETGID 能力。
注意:这个函数仅在 POSIX 平台上可用(例如,非Windows 和 Android)。
11initgroups(user, extra_group)
读取 /etc/group ,并初始化群组访问列表,使用成员所在的所有群组。这是授权操作,所以你需要有 root 权限,或者有 CAP_SETGID 能力。
注意:这个函数仅在 POSIX 平台上可用(例如,非Windows 和 Android)。
12kill(pid[, signal])
发送信号给进程. pid 是进程id,并且 signal 是发送的信号的字符串描述。信号名是字符串,比如 'SIGINT' 或 'SIGHUP'。如果忽略,信号会是 'SIGTERM'。
13memoryUsage()
返回一个对象,描述了 Node 进程所用的内存状况,单位为字节。
14nextTick(callback)
一旦当前事件循环结束,调用回调函数。
15umask([mask])
设置或读取进程文件的掩码。子进程从父进程继承掩码。如果mask 参数有效,返回旧的掩码。否则,返回当前掩码。
16uptime()
返回 Node 已经运行的秒数。
17hrtime()
返回当前进程的高分辨时间,形式为 [seconds, nanoseconds]数组。它是相对于过去的任意事件。该值与日期无关,因此不受时钟漂移的影响。主要用途是可以通过精确的时间间隔,来衡量程序的性能。
你可以将之前的结果传递给当前的 process.hrtime() ,会返回两者间的时间差,用来基准和测量时间间隔。

实例:

// 输出当前目录
console.log('当前目录: ' + process.cwd());

// 输出当前版本
console.log('当前版本: ' + process.version);

// 输出内存使用情况
console.log(process.memoryUsage());

十四、常用工具(util):

(1)语法:

const util = require('util');

(2)异步函数转回调:

//创建util对象
const util = require('util');

//创建异步函数
async function fn() {
  return 'hello world';
}
//异步转回调
const callbackFunction = util.callbackify(fn);

//回调函数
callbackFunction((err, ret) => {
  if (err) throw err;
  console.log(ret);
});

//判断err异常属性使用reason 
//判断无err异常
//err && err.hasOwnProperty('reason') && err.reason === null;  // true
//判断存在异常
//!(err && err.hasOwnProperty('reason') && err.reason === null);  // true

输出:

hello world

(3)基于原型继承:

//创建util对象
var util = require('util');

//创建 Base 原型
function Base() { 
    this.name = 'base'; 
    this.base = 1991; 
    this.sayHello = function() { 
    console.log('Hello ' + this.name); 
    }; 
} 

//创建 Base 原型方法
Base.prototype.showName = function() { 
    console.log(this.name);
}; 

//创建 Sub 原型
function Sub() { 
    this.name = 'sub'; 
} 

//Sub继承Base原型
util.inherits(Sub, Base); 
//实例化Base原型对象
var objBase = new Base(); 
//调用原型外部方法显示名字
objBase.showName(); 
//调用原型内部方法
objBase.sayHello(); 
//打印Base原型
console.log(objBase); 
//实例化Sub原型对象
var objSub = new Sub(); 
//调用继承自Base的原型外部方法显示名字
objSub.showName(); 
//objSub.sayHello(); 
//打印Sub原型
console.log(objSub); 

输出:

base
Hello base
Base { name: 'base', base: 1991, sayHello: [Function] }
sub
Sub { name: 'sub' }

(4)对象转化为字符串:

/**
语法:
util.inspect(object,[showHidden],[depth],[colors])
参数:
object 对象参数列表至少一个对象
showHidden 是一个可选参数,如果值为 true,将会输出更多隐藏信息。
depth 表示最大递归的层数,如果对象很复杂,你可以指定层数以控制输出信息的多 少。
如果不指定depth,默认会递归 2 层,指定为 null 表示将不限递归层数完整遍历对象。 
如果 colors 值为 true,输出格式将会以 ANSI 颜色编码,通常用于在终端显示更漂亮 的效果。
*/
var util = require('util'); 
function Person() { 
    this.name = 'byvoid'; 
    this.toString = function() { 
    return this.name; 
    }; 
} 
var obj = new Person(); 
console.log(util.inspect(obj)); 
console.log(util.inspect(obj, true)); 

输出:

Person { name: 'byvoid', toString: [Function] }
Person {
  name: 'byvoid',
  toString: [Function] {
    [length]: 0,
    [name]: '',
    [arguments]: null,
    [caller]: null,
    [prototype]: { [constructor]: [Circular] }
  }
}

(5)判断是否数组:

var util = require('util');

// 数组返回true 
console.log(util.isArray([]))
// 数组返回true  
console.log(util.isArray(new Array))
// 非数组返回false
console.log(util.isArray({}))

(6)判断是否是正则表达式:

var util = require('util');

// 正则表达式返回true
console.log(util.isRegExp(/some regexp/))
// 正则表达式返回true  
console.log(util.isRegExp(new RegExp('another regexp')))
// 非正则表达式返回false  
console.log(util.isRegExp({}))

(7)判断是否是日期:

var util = require('util');

// 日期格式返回true
console.log(util.isDate(new Date()))
// 非日期格式返回false (without 'new' returns a String)  
console.log(util.isDate(Date()))
// 非日期格式返回false  
console.log(util.isDate({}))

十五、文件系统(操作上类Unix):

(1)文件同步读取与异步读取:

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');
console.log("同步读取: " + data.toString());

console.log("程序执行完毕。");

(2)打开文件:

1、语法:

fs.open(path, flags[, mode], callback)

2、参数:

  • path - 文件的路径。

  • flags - 文件打开的行为。具体值详见下文。

  • mode - 设置文件模式(权限),文件创建默认权限为 0666(可读,可写)。

  • callback - 回调函数,带有两个参数如:callback(err, fd)。

flags 参数可以是以下值:

Flag描述
r以读取模式打开文件。如果文件不存在抛出异常。
r+以读写模式打开文件。如果文件不存在抛出异常。
rs以同步的方式读取文件。
rs+以同步的方式读取和写入文件。
w以写入模式打开文件,如果文件不存在则创建。
wx类似 'w',但是如果文件路径存在,则文件写入失败。
w+以读写模式打开文件,如果文件不存在则创建。
wx+类似 'w+', 但是如果文件路径存在,则文件读写失败。
a以追加模式打开文件,如果文件不存在则创建。
ax类似 'a', 但是如果文件路径存在,则文件追加失败。
a+以读取追加模式打开文件,如果文件不存在则创建。
ax+类似 'a+', 但是如果文件路径存在,则文件读取追加失败。

实例:

var fs = require("fs");

// 异步打开文件
console.log("准备打开文件!");
fs.open('input.txt', 'r+', function(err, fd) {
   if (err) {
       return console.error(err);
   }
  console.log("文件打开成功!");     
});

(3)获取文件信息:

1、语法:

fs.stat(path, callback)

2、参数:

  • path - 文件路径。

  • callback - 回调函数,带有两个参数如:(err, stats), stats 是 fs.Stats 对象。

3、实例:

var fs = require("fs");

console.log("准备打开文件!");
fs.stat('input.txt', function (err, stats) {
   if (err) {
       return console.error(err);
   }
   console.log(stats);
   console.log("读取文件信息成功!");
   
   // 检测文件类型
   console.log("是否为文件(isFile) ? " + stats.isFile());
   console.log("是否为目录(isDirectory) ? " + stats.isDirectory());    
});

4、stats类中的方法有:

方法描述
stats.isFile()如果是文件返回 true,否则返回 false。
stats.isDirectory()如果是目录返回 true,否则返回 false。
stats.isBlockDevice()如果是块设备返回 true,否则返回 false。
stats.isCharacterDevice()如果是字符设备返回 true,否则返回 false。
stats.isSymbolicLink()如果是软链接返回 true,否则返回 false。
stats.isFIFO()如果是FIFO,返回true,否则返回 false。FIFO是UNIX中的一种特殊类型的命令管道。
stats.isSocket()如果是 Socket 返回 true,否则返回 false。

(4)写入文件:

1、语法:

fs.writeFile(file, data[, options], callback)

//默认打开模式是w

2、参数:

  • file - 文件名或文件描述符。

  • data - 要写入文件的数据,可以是 String(字符串) 或 Buffer(缓冲) 对象。

  • options - 该参数是一个对象,包含 {encoding, mode, flag}。默认编码为 utf8, 模式为 0666 , flag 为 'w'

  • callback - 回调函数,回调函数只包含错误信息参数(err),在写入失败时返回。

3、实例:

var fs = require("fs");

console.log("准备写入文件");
fs.writeFile('input.txt', '我是通 过fs.writeFile 写入文件的内容',  function(err) {
   if (err) {
       return console.error(err);
   }
   console.log("数据写入成功!");
   console.log("--------我是分割线-------------")
   console.log("读取写入的数据!");
   fs.readFile('input.txt', function (err, data) {
      if (err) {
         return console.error(err);
      }
      console.log("异步读取文件数据: " + data.toString());
   });
});

(5)读取文件:

1、语法:

fs.read(fd, buffer, offset, length, position, callback)

2、参数:

  • fd - 通过 fs.open() 方法返回的文件描述符。

  • buffer - 数据写入的缓冲区。

  • offset - 缓冲区写入的写入偏移量。

  • length - 要从文件中读取的字节数。

  • position - 文件读取的起始位置,如果 position 的值为 null,则会从当前文件指针的位置读取。

  • callback - 回调函数,有三个参数err, bytesRead, buffer,err 为错误信息, bytesRead 表示读取的字节数,buffer 为缓冲区对象。

3、实例:

var fs = require("fs");
var buf = new Buffer.alloc(1024);

console.log("准备打开已存在的文件!");
fs.open('input.txt', 'r+', function(err, fd) {
   if (err) {
       return console.error(err);
   }
   console.log("文件打开成功!");
   console.log("准备读取文件:");
   fs.read(fd, buf, 0, buf.length, 0, function(err, bytes){
      if (err){
         console.log(err);
      }
      console.log(bytes + "  字节被读取");
      
      // 仅输出读取的字节
      if(bytes > 0){
         console.log(buf.slice(0, bytes).toString());
      }
   });
});

(6)关闭文件:

1、语法:

fs.close(fd, callback)

2、参数:

  • fd - 通过 fs.open() 方法返回的文件描述符。

  • callback - 回调函数,没有参数。

3、实例:

var fs = require("fs");
var buf = new Buffer.alloc(1024);

console.log("准备打开文件!");
fs.open('input.txt', 'r+', function(err, fd) {
   if (err) {
       return console.error(err);
   }
   console.log("文件打开成功!");
   console.log("准备读取文件!");
   fs.read(fd, buf, 0, buf.length, 0, function(err, bytes){
      if (err){
         console.log(err);
      }

      // 仅输出读取的字节
      if(bytes > 0){
         console.log(buf.slice(0, bytes).toString());
      }

      // 关闭文件
      fs.close(fd, function(err){
         if (err){
            console.log(err);
         } 
         console.log("文件关闭成功");
      });
   });
});

(7)截取文件:

1、语法:

fs.ftruncate(fd, len, callback)

2、参数:

  • fd - 通过 fs.open() 方法返回的文件描述符。

  • len - 文件内容截取的长度。

  • callback - 回调函数,没有参数。

3、实例:

var fs = require("fs");
var buf = new Buffer.alloc(1024);

console.log("准备打开文件!");
fs.open('input.txt', 'r+', function(err, fd) {
   if (err) {
       return console.error(err);
   }
   console.log("文件打开成功!");
   console.log("截取10字节内的文件内容,超出部分将被去除。");
   
   // 截取文件
   fs.ftruncate(fd, 10, function(err){
      if (err){
         console.log(err);
      } 
      console.log("文件截取成功。");
      console.log("读取相同的文件"); 
      fs.read(fd, buf, 0, buf.length, 0, function(err, bytes){
         if (err){
            console.log(err);
         }

         // 仅输出读取的字节
         if(bytes > 0){
            console.log(buf.slice(0, bytes).toString());
         }

         // 关闭文件
         fs.close(fd, function(err){
            if (err){
               console.log(err);
            } 
            console.log("文件关闭成功!");
         });
      });
   });
});

(8)删除文件:

1、语法:

fs.unlink(path, callback)

2、参数:

  • path - 文件路径。

  • callback - 回调函数,没有参数。

3、实例:

var fs = require("fs");

console.log("准备删除文件!");
fs.unlink('input.txt', function(err) {
   if (err) {
       return console.error(err);
   }
   console.log("文件删除成功!");
});

(9)创建目录:

1、语法:

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

2、参数:

  • path - 文件路径。

  • options 参数可以是:

    • recursive - 是否以递归的方式创建目录,默认为 false。
    • mode - 设置目录权限,默认为 0777。
  • callback - 回调函数,没有参数。

3、实例:

var fs = require("fs");
// tmp 目录必须存在
console.log("创建目录 /tmp/test/");
fs.mkdir("/tmp/test/",function(err){
   if (err) {
       return console.error(err);
   }
   console.log("目录创建成功。");
});

(10)读取目录:

1、读取:

fs.readdir(path, callback)

2、参数:

  • path - 文件路径。

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

3、实例:

var fs = require("fs");

console.log("查看 /tmp 目录");
fs.readdir("/tmp/",function(err, files){
   if (err) {
       return console.error(err);
   }
   files.forEach( function (file){
       console.log( file );
   });
});

 (11)删除目录:

1、语法:

fs.rmdir(path, callback)

2、参数:

  • path - 文件路径。

  • callback - 回调函数,没有参数。

3、实例:

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 );
      });
   });
});

(12)文件模块方法参考手册:

序号方法 & 描述
1fs.rename(oldPath, newPath, callback)
异步 rename().回调函数没有参数,但可能抛出异常。
2fs.ftruncate(fd, len, callback)
异步 ftruncate().回调函数没有参数,但可能抛出异常。
3fs.ftruncateSync(fd, len)
同步 ftruncate()
4fs.truncate(path, len, callback)
异步 truncate().回调函数没有参数,但可能抛出异常。
5fs.truncateSync(path, len)
同步 truncate()
6fs.chown(path, uid, gid, callback)
异步 chown().回调函数没有参数,但可能抛出异常。
7fs.chownSync(path, uid, gid)
同步 chown()
8fs.fchown(fd, uid, gid, callback)
异步 fchown().回调函数没有参数,但可能抛出异常。
9fs.fchownSync(fd, uid, gid)
同步 fchown()
10fs.lchown(path, uid, gid, callback)
异步 lchown().回调函数没有参数,但可能抛出异常。
11fs.lchownSync(path, uid, gid)
同步 lchown()
12fs.chmod(path, mode, callback)
异步 chmod().回调函数没有参数,但可能抛出异常。
13fs.chmodSync(path, mode)
同步 chmod().
14fs.fchmod(fd, mode, callback)
异步 fchmod().回调函数没有参数,但可能抛出异常。
15fs.fchmodSync(fd, mode)
同步 fchmod().
16fs.lchmod(path, mode, callback)
异步 lchmod().回调函数没有参数,但可能抛出异常。Only available on Mac OS X.
17fs.lchmodSync(path, mode)
同步 lchmod().
18fs.stat(path, callback)
异步 stat(). 回调函数有两个参数 err, stats,stats 是 fs.Stats 对象。
19fs.lstat(path, callback)
异步 lstat(). 回调函数有两个参数 err, stats,stats 是 fs.Stats 对象。
20fs.fstat(fd, callback)
异步 fstat(). 回调函数有两个参数 err, stats,stats 是 fs.Stats 对象。
21fs.statSync(path)
同步 stat(). 返回 fs.Stats 的实例。
22fs.lstatSync(path)
同步 lstat(). 返回 fs.Stats 的实例。
23fs.fstatSync(fd)
同步 fstat(). 返回 fs.Stats 的实例。
24fs.link(srcpath, dstpath, callback)
异步 link().回调函数没有参数,但可能抛出异常。
25fs.linkSync(srcpath, dstpath)
同步 link().
26fs.symlink(srcpath, dstpath[, type], callback)
异步 symlink().回调函数没有参数,但可能抛出异常。 type 参数可以设置为 'dir', 'file', 或 'junction' (默认为 'file') 。
27fs.symlinkSync(srcpath, dstpath[, type])
同步 symlink().
28fs.readlink(path, callback)
异步 readlink(). 回调函数有两个参数 err, linkString。
29fs.realpath(path[, cache], callback)
异步 realpath(). 回调函数有两个参数 err, resolvedPath。
30fs.realpathSync(path[, cache])
同步 realpath()。返回绝对路径。
31fs.unlink(path, callback)
异步 unlink().回调函数没有参数,但可能抛出异常。
32fs.unlinkSync(path)
同步 unlink().
33fs.rmdir(path, callback)
异步 rmdir().回调函数没有参数,但可能抛出异常。
34fs.rmdirSync(path)
同步 rmdir().
35fs.mkdir(path[, mode], callback)
S异步 mkdir(2).回调函数没有参数,但可能抛出异常。 访问权限默认为 0777。
36fs.mkdirSync(path[, mode])
同步 mkdir().
37fs.readdir(path, callback)
异步 readdir(3). 读取目录的内容。
38fs.readdirSync(path)
同步 readdir().返回文件数组列表。
39fs.close(fd, callback)
异步 close().回调函数没有参数,但可能抛出异常。
40fs.closeSync(fd)
同步 close().
41fs.open(path, flags[, mode], callback)
异步打开文件。
42fs.openSync(path, flags[, mode])
同步 version of fs.open().
43fs.utimes(path, atime, mtime, callback)
 
44fs.utimesSync(path, atime, mtime)
修改文件时间戳,文件通过指定的文件路径。
45fs.futimes(fd, atime, mtime, callback)
 
46fs.futimesSync(fd, atime, mtime)
修改文件时间戳,通过文件描述符指定。
47fs.fsync(fd, callback)
异步 fsync.回调函数没有参数,但可能抛出异常。
48fs.fsyncSync(fd)
同步 fsync.
49fs.write(fd, buffer, offset, length[, position], callback)
将缓冲区内容写入到通过文件描述符指定的文件。
50fs.write(fd, data[, position[, encoding]], callback)
通过文件描述符 fd 写入文件内容。
51fs.writeSync(fd, buffer, offset, length[, position])
同步版的 fs.write()。
52fs.writeSync(fd, data[, position[, encoding]])
同步版的 fs.write().
53fs.read(fd, buffer, offset, length, position, callback)
通过文件描述符 fd 读取文件内容。
54fs.readSync(fd, buffer, offset, length, position)
同步版的 fs.read.
55fs.readFile(filename[, options], callback)
异步读取文件内容。
56fs.readFileSync(filename[, options])
57fs.writeFile(filename, data[, options], callback)
异步写入文件内容。
58fs.writeFileSync(filename, data[, options])
同步版的 fs.writeFile。
59fs.appendFile(filename, data[, options], callback)
异步追加文件内容。
60fs.appendFileSync(filename, data[, options])
The 同步 version of fs.appendFile.
61fs.watchFile(filename[, options], listener)
查看文件的修改。
62fs.unwatchFile(filename[, listener])
停止查看 filename 的修改。
63fs.watch(filename[, options][, listener])
查看 filename 的修改,filename 可以是文件或目录。返回 fs.FSWatcher 对象。
64fs.exists(path, callback)
检测给定的路径是否存在。
65fs.existsSync(path)
同步版的 fs.exists.
66fs.access(path[, mode], callback)
测试指定路径用户权限。
67fs.accessSync(path[, mode])
同步版的 fs.access。
68fs.createReadStream(path[, options])
返回ReadStream 对象。
69fs.createWriteStream(path[, options])
返回 WriteStream 对象。
70fs.symlink(srcpath, dstpath[, type], callback)
异步 symlink().回调函数没有参数,但可能抛出异常。

十五、Node.js GET/POST请求:

(1)获取GET请求内容:

//获取http/url/util模块
var http = require('http');
var url = require('url');
var util = require('util');

//创建Server
http.createServer(function(req, res){
    //定义HTTP文本
    res.writeHead(200, {'Content-Type': 'text/plain; charset=utf-8'});
    //原型继承解析url
    res.end(util.inspect(url.parse(req.url, true)));
}).listen(3000);//监听端口

(2)获取 URL 的参数:

//获取http/url/util模块

var http = require('http');
var url = require('url');
var util = require('util');
 
http.createServer(function(req, res){
    res.writeHead(200, {'Content-Type': 'text/plain'});
 
    // 解析 url 参数
    var params = url.parse(req.url, true).query;
    res.write("网站名:" + params.name);
    res.write("\n");
    res.write("网站 URL:" + params.url);
    res.end();
 
}).listen(3000);

(3)获取 POST 请求内容:

//获取http/querystring/util模块
var http = require('http');
var querystring = require('querystring');
var util = require('util');
 
http.createServer(function(req, res){
    // 定义了一个post变量,用于暂存请求体的信息
    var post = '';     
 
    // 通过req的data事件监听函数,每当接受到请求体的数据,就累加到post变量中
    req.on('data', function(chunk){    
        post += chunk;
    });
 
    // 在end事件触发后,通过querystring.parse将post解析为真正的POST请求格式,然后向客户端返回。
    req.on('end', function(){    
        post = querystring.parse(post);
        res.end(util.inspect(post));
    });
}).listen(3000);

(4)获取 POST 请求内容并解析参数:

//获取http/querystring模块
var http = require('http');
var querystring = require('querystring');
 
var postHTML = 
  '<html><head><meta charset="utf-8"><title>菜鸟教程 Node.js 实例</title></head>' +
  '<body>' +
  '<form method="post">' +
  '网站名: <input name="name"><br>' +
  '网站 URL: <input name="url"><br>' +
  '<input type="submit">' +
  '</form>' +
  '</body></html>';
 
http.createServer(function (req, res) {
  var body = "";
  req.on('data', function (chunk) {
    body += chunk;
  });
  req.on('end', function () {
    // 解析参数
    body = querystring.parse(body);
    // 设置响应头部信息及编码
    res.writeHead(200, {'Content-Type': 'text/html; charset=utf8'});
 
    if(body.name && body.url) { // 输出提交的数据
        res.write("网站名:" + body.name);
        res.write("<br>");
        res.write("网站 URL:" + body.url);
    } else {  // 输出表单
        res.write(postHTML);
    }
    res.end();
  });
}).listen(3000);

十六、Node.js 工具模块:

序号模块名 & 描述
1OS 模块
提供基本的系统操作函数。
2Path 模块
提供了处理和转换文件路径的工具。
3Net 模块
用于底层的网络通信。提供了服务端和客户端的的操作。
4

DNS 模块
用于解析域名。

5Domain 模块
简化异步代码的异常处理,可以捕捉处理try catch无法捕捉的。

 十七、Node.js Web 模块:

(1)Web 应用架构:

  • Client - 客户端,一般指浏览器,浏览器可以通过 HTTP 协议向服务器请求数据。

  • Server - 服务端,一般指 Web 服务器,可以接收客户端请求,并向客户端发送响应数据。

  • Business - 业务层, 通过 Web 服务器处理应用程序,如与数据库交互,逻辑运算,调用外部程序等。

  • Data - 数据层,一般由数据库组成。

(2)使用 Node 创建 Web 服务器:

server.js:

//请求http/fs/url模块
var http = require('http');
var fs = require('fs');
var url = require('url');
 
 
// 创建服务器
http.createServer( function (request, response) {  
   // 解析请求,包括文件名
   var pathname = url.parse(request.url).pathname;
   
   // 输出请求的文件名
   console.log("Request for " + pathname + " received.");
   
   // 从文件系统中读取请求的文件内容
   fs.readFile(pathname.substr(1), function (err, data) {
      if (err) {
         console.log(err);
         // HTTP 状态码: 404 : NOT FOUND
         // Content Type: text/html
         response.writeHead(404, {'Content-Type': 'text/html; charset=utf8'});
      }else{             
         // HTTP 状态码: 200 : OK
         // Content Type: text/html
         response.writeHead(200, {'Content-Type': 'text/html; charset=utf8'});    
         
         // 响应文件内容
         response.write(data.toString());        
      }
      //  发送响应数据
      response.end();
   });   
}).listen(8080);
 
// 控制台会输出以下信息
console.log('Server running at http://127.0.0.1:8080/');

index.html:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>我的第一个Node.js文本(buoy2019.com)</title>
</head>
<body>
    <h1>我的第一个标题</h1>
    <p>我的第一个段落。</p>
</body>
</html>

client.js:


var http = require('http');
 
// 用于请求的选项
var options = {
   host: 'localhost',
   port: '8080',
   path: '/index.html'  
};
 
// 处理响应的回调函数
var callback = function(response){
   // 不断更新数据
   var body = '';
   response.on('data', function(data) {
      body += data;
   });
   
   response.on('end', function() {
      // 数据接收完成
      console.log(body);
   });
}
// 向服务端发送请求
var req = http.request(options, callback);
req.end();

十八、Node.js Express 框架:

(1)概念:

Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。

使用 Express 可以快速地搭建一个完整功能的网站。

Express 框架核心特性:

  • 可以设置中间件来响应 HTTP 请求。

  • 定义了路由表用于执行不同的 HTTP 请求动作。

  • 可以通过向模板传递参数来动态渲染 HTML 页面。

(2)安装Express:

./node npm install express --save

以上命令会将 Express 框架安装在当前目录的 node_modules 目录中, node_modules 目录下会自动创建 express 目录。以下几个重要的模块是需要与 express 框架一起安装的:

    body-parser - node.js 中间件,用于处理 JSON, Raw, Text 和 URL 编码的数据。

    cookie-parser - 这就是一个解析Cookie的工具。通过req.cookies可以取到传过来的cookie,并把它们转成对象。

    multer - node.js 中间件,用于处理 enctype="multipart/form-data"(设置表单的MIME编码)的表单数据。

./node npm install body-parser --save
./node npm install cookie-parser --save
./node npm install multer --save

(3)第一个 Express 框架实例:

//express_demo.js 文件
var express = require('express');
//实例化express
var app = express();

//定义Contrallor 
app.get('/', function (req, res) {
   res.send('Hello World');
})

//监听端口
var server = app.listen(8081, function () {
 
  var host = server.address().address
  var port = server.address().port
 
  console.log("应用实例,访问地址为 http://%s:%s", host, port)
 
})

(4)请求和响应:

1、语法:

//req请求,res响应
app.get('/', function (req, res) {
   // --
})

Request 对象 - request 对象表示 HTTP 请求,包含了请求查询字符串,参数,内容,HTTP 头部等属性。常见属性有:

  1. req.app:当callback为外部文件时,用req.app访问express的实例
  2. req.baseUrl:获取路由当前安装的URL路径
  3. req.body / req.cookies:获得「请求主体」/ Cookies
  4. req.fresh / req.stale:判断请求是否还「新鲜」
  5. req.hostname / req.ip:获取主机名和IP地址
  6. req.originalUrl:获取原始请求URL
  7. req.params:获取路由的parameters
  8. req.path:获取请求路径
  9. req.protocol:获取协议类型
  10. req.query:获取URL的查询参数串
  11. req.route:获取当前匹配的路由
  12. req.subdomains:获取子域名
  13. req.accepts():检查可接受的请求的文档类型
  14. req.acceptsCharsets / req.acceptsEncodings / req.acceptsLanguages:返回指定字符集的第一个可接受字符编码
  15. req.get():获取指定的HTTP请求头
  16. req.is():判断请求头Content-Type的MIME类型

Response 对象 - response 对象表示 HTTP 响应,即在接收到请求时向客户端发送的 HTTP 响应数据。常见属性有:

  1. res.app:同req.app一样
  2. res.append():追加指定HTTP头
  3. res.set()在res.append()后将重置之前设置的头
  4. res.cookie(name,value [,option]):设置Cookie
  5. opition: domain / expires / httpOnly / maxAge / path / secure / signed
  6. res.clearCookie():清除Cookie
  7. res.download():传送指定路径的文件
  8. res.get():返回指定的HTTP头
  9. res.json():传送JSON响应
  10. res.jsonp():传送JSONP响应
  11. res.location():只设置响应的Location HTTP头,不设置状态码或者close response
  12. res.redirect():设置响应的Location HTTP头,并且设置状态码302
  13. res.render(view,[locals],callback):渲染一个view,同时向callback传递渲染后的字符串,如果在渲染过程中有错误发生next(err)将会被自动调用。callback将会被传入一个可能发生的错误以及渲染后的页面,这样就不会自动输出了。
  14. res.send():传送HTTP响应
  15. res.sendFile(path [,options] [,fn]):传送指定路径的文件 -会自动根据文件extension设定Content-Type
  16. res.set():设置HTTP头,传入object可以一次设置多个头
  17. res.status():设置HTTP状态码
  18. res.type():设置Content-Type的MIME类型

(5)路由:


var express = require('express');
var app = express();
 
//  主页输出 "Hello World"
app.get('/', function (req, res) {
   console.log("主页 GET 请求");
   res.send('Hello GET');
})
 
 
//  POST 请求
app.post('/', function (req, res) {
   console.log("主页 POST 请求");
   res.send('Hello POST');
})
 
//  /del_user 页面响应
app.get('/del_user', function (req, res) {
   console.log("/del_user 响应 DELETE 请求");
   res.send('删除页面');
})
 
//  /list_user 页面 GET 请求
app.get('/list_user', function (req, res) {
   console.log("/list_user GET 请求");
   res.send('用户列表页面');
})
 
// 对页面 abcd, abxcd, ab123cd, 等响应 GET 请求
app.get('/ab*cd', function(req, res) {   
   console.log("/ab*cd GET 请求");
   res.send('正则匹配');
})
 
 
var server = app.listen(8081, function () {
 
  var host = server.address().address
  var port = server.address().port
 
  console.log("应用实例,访问地址为 http://%s:%s", host, port)
 
})

(6)静态文件:


var express = require('express');
var app = express();

//获取文件位置,地址栏输入127.0.0.1:8081/public/logo.png即可访问到public/logo.png文件
app.use('/public', express.static('public'));
 
app.get('/', function (req, res) {
   res.send('Hello World');
})
 
var server = app.listen(8081, function () {
 
  var host = server.address().address
  var port = server.address().port
 
  console.log("应用实例,访问地址为 http://%s:%s", host, port)
 
})

(6)GET 方法:

index.html

<html>
<body>
<form action="http://127.0.0.1:8081/process_get" method="GET">
First Name: <input type="text" name="first_name">  <br>
 
Last Name: <input type="text" name="last_name">
<input type="submit" value="Submit">
</form>
</body>
</html>

server.js:


var express = require('express');
var app = express();

//访问静态文件
app.use('/public', express.static('public'));

//路由
app.get('/index.htm', function (req, res) {
   //获取__dirname目录中index.html文件
   res.sendFile( __dirname + "/" + "index.html" );
})
 
app.get('/process_get', function (req, res) {
 
   // 输出 JSON 格式
   var response = {
       "first_name":req.query.first_name,
       "last_name":req.query.last_name
   };
   console.log(response);
   res.end(JSON.stringify(response));
})
 
var server = app.listen(8081, function () {
 
  var host = server.address().address
  var port = server.address().port
 
  console.log("应用实例,访问地址为 http://%s:%s", host, port)
 
})

(7)POST方法:

index01.html:

<html>
<body>
<form action="http://127.0.0.1:8081/process_post" method="POST">
First Name: <input type="text" name="first_name">  <br>
 
Last Name: <input type="text" name="last_name">
<input type="submit" value="Submit">
</form>
</body>
</html>

server01.js:


var express = require('express');
var app = express();
var bodyParser = require('body-parser');
 
// 创建 application/x-www-form-urlencoded 编码解析
// Get与Post方法区别
var urlencodedParser = bodyParser.urlencoded({ extended: false })
 
app.use('/public', express.static('public'));
 
app.get('/index01.html', function (req, res) {
   res.sendFile( __dirname + "/" + "index01.html" );
})
 
app.post('/process_post', urlencodedParser, function (req, res) {
 
   // 输出 JSON 格式
   var response = {
       "first_name":req.body.first_name,
       "last_name":req.body.last_name
   };
   console.log(response);
   res.end(JSON.stringify(response));
})
 
var server = app.listen(8081, function () {
 
  var host = server.address().address
  var port = server.address().port
 
  console.log("应用实例,访问地址为 http://%s:%s", host, port)
 
})

(8)文件上传:

index02.html:

<html>
<head>
<title>文件上传表单</title>
</head>
<body>
<h3>文件上传:</h3>
选择一个文件上传: <br />
<form action="/file_upload" method="post" enctype="multipart/form-data">
<input type="file" name="image" size="50" />
<br />
<input type="submit" value="上传文件" />
</form>
</body>
</html>

server02.js:(需要安装multer)

var express = require('express');
var app = express();
var fs = require("fs");
 
var bodyParser = require('body-parser');
var multer  = require('multer');
 
app.use('/public', express.static('public'));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(multer({ dest: '/tmp/'}).array('image'));
 
app.get('/index02.html', function (req, res) {
   res.sendFile( __dirname + "/" + "index02.html" );
})
 
app.post('/file_upload', function (req, res) {
 
   console.log(req.files[0]);  // 上传的文件信息
 
   var des_file = __dirname + "/" + req.files[0].originalname;
   fs.readFile( req.files[0].path, function (err, data) {
        fs.writeFile(des_file, data, function (err) {
         if( err ){
              console.log( err );
         }else{
               response = {
                   message:'File uploaded successfully', 
                   filename:req.files[0].originalname
              };
          }
          console.log( response );
          res.end( JSON.stringify( response ) );
       });
   });
})
 
var server = app.listen(8081, function () {
 
  var host = server.address().address
  var port = server.address().port
 
  console.log("应用实例,访问地址为 http://%s:%s", host, port)
 
})

(9)Cookie 管理:

express_cookie.js(需要安装cookie-parser)

// express_cookie.js 文件
var express      = require('express')
var cookieParser = require('cookie-parser')
var util = require('util');
 
var app = express()
app.use(cookieParser())
 
app.get('/', function(req, res) {
    console.log("Cookies: " + util.inspect(req.cookies));
})
 
app.listen(8081)

(10)Express扩展:

十九、Node.js RESTful API:

(1)常用方法:

  • GET - 用于获取数据。

  • PUT - 用于更新或添加数据。

  • DELETE - 用于删除数据。

  • POST - 用于添加数据。

(2)创建 RESTful:

users.json:

{
   "user1" : {
      "name" : "mahesh",
      "password" : "password1",
      "profession" : "teacher",
      "id": 1
   },
   "user2" : {
      "name" : "suresh",
      "password" : "password2",
      "profession" : "librarian",
      "id": 2
   },
   "user3" : {
      "name" : "ramesh",
      "password" : "password3",
      "profession" : "clerk",
      "id": 3
   }
}

(3)获取用户列表:

listUsers.js

var express = require('express');
var app = express();
var fs = require("fs");

app.get('/listUsers', function (req, res) {
   fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {
       console.log( data );
       res.end( data );
   });
})

var server = app.listen(8081, function () {

  var host = server.address().address
  var port = server.address().port

  console.log("应用实例,访问地址为 http://%s:%s", host, port)

})

(4)添加用户:

addUser.js:

var express = require('express');
var app = express();
var fs = require("fs");

//添加的新用户数据
var user = {
   "user4" : {
      "name" : "mohit",
      "password" : "password4",
      "profession" : "teacher",
      "id": 4
   }
}

app.get('/addUser', function (req, res) {
   // 读取已存在的数据
   fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {
       data = JSON.parse( data );
       data["user4"] = user["user4"];
       console.log( data );
       res.end( JSON.stringify(data));
   });
})

var server = app.listen(8081, function () {

  var host = server.address().address
  var port = server.address().port
  console.log("应用实例,访问地址为 http://%s:%s", host, port)

})

(5)显示用户详情:

show.js

var express = require('express');
var app = express();
var fs = require("fs");

//输入用户id,/:id模式匹配
app.get('/:id', function (req, res) {
   // 首先我们读取已存在的用户
   fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {
       data = JSON.parse( data );
       //条件查询user+id
       var user = data["user" + req.params.id] 
       console.log( user );
       res.end( JSON.stringify(user));
   });
})

var server = app.listen(8081, function () {

  var host = server.address().address
  var port = server.address().port
  console.log("应用实例,访问地址为 http://%s:%s", host, port)

})

(6)删除用户:

deleteUser.js

var express = require('express');
var app = express();
var fs = require("fs");

var id = 2;

app.get('/deleteUser', function (req, res) {

   // 读取已经存在的json数据
   fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {
       data = JSON.parse( data );
       
       //删除data数据(JS原生SQL操作)
       delete data["user" + id];
       
       console.log( data );
       res.end( JSON.stringify(data));
   });
})

var server = app.listen(8081, function () {

  var host = server.address().address
  var port = server.address().port
  console.log("应用实例,访问地址为 http://%s:%s", host, port)

})

二十、Node.js 多进程:

(1)原理:

利用事件驱动处理并发在多核 cpu 的系统上创建多个子进程,从而提高性能。补充Node.js单线程缺陷。

每个子进程总是带有三个流对象:child.stdin, child.stdout 和child.stderr。他们可能会共享父进程的 stdio 流,或者也可以是独立的被导流的流对象。

(2)创建多线程方法:(依赖child_process)

  • exec - child_process.exec 使用子进程执行命令,缓存子进程的输出,并将子进程的输出以回调函数参数的形式返回。

  • spawn - child_process.spawn 使用指定的命令行参数创建新进程。

  • fork - child_process.fork 是 spawn()的特殊形式,用于在子进程中运行的模块,如 fork('./son.js') 相当于 spawn('node', ['./son.js']) 。与spawn方法不同的是,fork会在父进程与子进程之间,建立一个通信管道,用于进程之间的通信。

(3)exec() 方法:(缓存子进程的输出)

语法:

child_process.exec(command[, options], callback)

参数:

command: 字符串, 将要运行的命令,参数使用空格隔开

options :对象,可以是:

  • cwd ,字符串,子进程的当前工作目录
  • env,对象 环境变量键值对
  • encoding ,字符串,字符编码(默认: 'utf8')
  • shell ,字符串,将要执行命令的 Shell(默认: 在 UNIX 中为/bin/sh, 在 Windows 中为cmd.exe, Shell 应当能识别 -c开关在 UNIX 中,或 /s /c 在 Windows 中。 在Windows 中,命令行解析应当能兼容cmd.exe
  • timeout,数字,超时时间(默认: 0)
  • maxBuffer,数字, 在 stdout 或 stderr 中允许存在的最大缓冲(二进制),如果超出那么子进程将会被杀死 (默认: 200*1024)
  • killSignal ,字符串,结束信号(默认:'SIGTERM')
  • uid,数字,设置用户进程的 ID
  • gid,数字,设置进程组的 ID

callback :回调函数,包含三个参数error, stdout 和 stderr。

exec() 方法返回最大的缓冲区,并等待进程结束,一次性返回缓冲区的内容。

实例:

support.js

console.log("进程 " + process.argv[2] + " 执行。" );

master.js


const fs = require('fs');
const child_process = require('child_process');
 
for(var i=0; i<3; i++) {
    var workerProcess = child_process.exec('../node support.js '+i, function (error, stdout, stderr) {
        if (error) {
            console.log(error.stack);
            console.log('Error code: '+error.code);
            console.log('Signal received: '+error.signal);
        }
        console.log('stdout: ' + stdout);
        console.log('stderr: ' + stderr);
    });
 
    workerProcess.on('exit', function (code) {
        console.log('子进程已退出,退出码 '+code);
    });
}

(4)spawn() 方法:(使用指定的命令行参数创建新进程,适合大量数据)

1、语法:

child_process.spawn(command[, args][, options])

2、参数:

command: 将要运行的命令

args: Array 字符串参数数组

options Object

  • cwd String 子进程的当前工作目录
  • env Object 环境变量键值对
  • stdio Array|String 子进程的 stdio 配置
  • detached Boolean 这个子进程将会变成进程组的领导
  • uid Number 设置用户进程的 ID
  • gid Number 设置进程组的 ID

spawn() 方法返回流 (stdout & stderr),在进程返回大量数据时使用。进程一旦开始执行时 spawn() 就开始接收响应。

3、实例:

support01.js:

//获得process第二个参数即序号
console.log("进程 " + process.argv[2] + " 执行。" );

master01.js:

const fs = require('fs');
const child_process = require('child_process');
 
for(var i=0; i<3; i++) {
   var workerProcess = child_process.spawn('../node', ['support01.js', i]);
   //执行成功返回函数
   workerProcess.stdout.on('data', function (data) {
      console.log('stdout: ' + data);
   });
   //执行失败返回函数
   workerProcess.stderr.on('data', function (data) {
      console.log('stderr: ' + data);
   });
   //绑定执行函数
   workerProcess.on('close', function (code) {
      console.log('子进程已退出,退出码 '+code);
   });
}

(5)fork 方法:(管道线程,用在对即时性要求高的情景)

1、语法:

child_process.fork(modulePath[, args][, options])

2、参数:

modulePath: String,将要在子进程中运行的模块

args: Array 字符串参数数组

options:Object

  • cwd String 子进程的当前工作目录
  • env Object 环境变量键值对
  • execPath String 创建子进程的可执行文件
  • execArgv Array 子进程的可执行文件的字符串参数数组(默认: process.execArgv)
  • silent Boolean 如果为true,子进程的stdinstdoutstderr将会被关联至父进程,否则,它们将会从父进程中继承。(默认为:false
  • uid Number 设置用户进程的 ID
  • gid Number 设置进程组的 ID

返回的对象除了拥有ChildProcess实例的所有方法,还有一个内建的通信信道。

3、实例:

support02.js

console.log("进程 " + process.argv[2] + " 执行。" );

master02.js

const fs = require('fs');
const child_process = require('child_process');
 
for(var i=0; i<3; i++) {
   var worker_process = child_process.fork("support02.js", [i]);    
 
   worker_process.on('close', function (code) {
      console.log('子进程已退出,退出码 ' + code);
   });
}

二十一、Node.js JXcore 打包:(待定)

(1)概念:

Node.js 是一个开放源代码、跨平台的、用于服务器端和网络应用的运行环境。

JXcore 是一个支持多线程的 Node.js 发行版本,基本不需要对你现有的代码做任何改动就可以直接线程安全地以多线程运行。

这篇文章主要是要向大家介绍 JXcore 的打包功能。

(2)安装:

二十二、连接MySQL:(需要安装node.js的mysql模块)

(1)连接数据库:


var mysql      = require('mysql');
var connection = mysql.createConnection({
  host     : 'localhost',
  user     : 'root',
  password : 'root',
  database : 'test'
});
 
connection.connect();
 
connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
  if (error) throw error;
  //获取连接条数
  console.log('The solution is: ', results[0].solution);
});

 

数据库连接参数说明:

参数描述
host主机地址 (默认:localhost)
  user用户名
  password密码
  port端口号 (默认:3306)
  database数据库名
  charset连接字符集(默认:'UTF8_GENERAL_CI',注意字符集的字母都要大写)
  localAddress此IP用于TCP连接(可选)
  socketPath连接到unix域路径,当使用 host 和 port 时会被忽略
  timezone时区(默认:'local')
  connectTimeout连接超时(默认:不限制;单位:毫秒)
  stringifyObjects是否序列化对象
  typeCast是否将列值转化为本地JavaScript类型值 (默认:true)
  queryFormat自定义query语句格式化方法
  supportBigNumbers数据库支持bigint或decimal类型列时,需要设此option为true (默认:false)
  bigNumberStringssupportBigNumbers和bigNumberStrings启用 强制bigint或decimal列以JavaScript字符串类型返回(默认:false)
  dateStrings强制timestamp,datetime,data类型以字符串类型返回,而不是JavaScript Date类型(默认:false)
  debug开启调试(默认:false)
  multipleStatements是否许一个query中有多个MySQL语句 (默认:false)
  flags用于修改连接标志
  ssl使用ssl参数(与crypto.createCredenitals参数格式一至)或一个包含ssl配置文件名称的字符串,目前只捆绑Amazon RDS的配置文件

更多说明可参见:https://github.com/mysqljs/mysql

(2)插入数据:

var mysql  = require('mysql');  
 
var connection = mysql.createConnection({     
  host     : 'localhost',       
  user     : 'root',              
  password : 'root',       
  port: '3306',                   
  database: 'test' 
}); 
 
connection.connect();
//模式匹配values参数 
var addSql = 'INSERT INTO websites(Id,name,url,alexa,country) VALUES(0,?,?,?,?)';
var addSqlParams = ['buoy2019', 'https://buoy2019.com','23453', 'CN'];
//执行添加语句
connection.query(addSql,addSqlParams,function (err, result) {
        if(err){
         console.log('[INSERT ERROR] - ',err.message);
         return;
        }        
 
       console.log('--------------------------INSERT----------------------------');
       console.log('INSERT ID:',result.insertId);        
       console.log('INSERT ID:',result);        
       console.log('-----------------------------------------------------------------\n\n');  
});
 
connection.end();

(3)查询数据:


var mysql  = require('mysql');  
 
var connection = mysql.createConnection({     
  host     : 'localhost',       
  user     : 'root',              
  password : 'root',       
  port: '3306',                   
  database: 'test' 
}); 
 
connection.connect();
 
var  sql = 'SELECT * FROM websites';
//查
connection.query(sql,function (err, result) {
        if(err){
          console.log('[SELECT ERROR] - ',err.message);
          return;
        }
 
       console.log('--------------------------SELECT----------------------------');
       console.log(result);
       console.log('------------------------------------------------------------\n\n');  
});
 
connection.end();

(4)更新数据:


var mysql  = require('mysql');  
 
var connection = mysql.createConnection({     
  host     : 'localhost',       
  user     : 'root',              
  password : 'root',       
  port: '3306',                   
  database: 'test' 
}); 
 
connection.connect();
 
var modSql = 'UPDATE websites SET name = ?,url = ? WHERE Id = ?';
var modSqlParams = ['dfg', 'https://m.runoob.com',6];
//改
connection.query(modSql,modSqlParams,function (err, result) {
   if(err){
         console.log('[UPDATE ERROR] - ',err.message);
         return;
   }        
  console.log('--------------------------UPDATE----------------------------');
  console.log('UPDATE affectedRows',result.affectedRows);
  console.log('-----------------------------------------------------------------\n\n');
});
 
connection.end();

(5)删除数据:


var mysql  = require('mysql');  
 
var connection = mysql.createConnection({     
  host     : 'localhost',       
  user     : 'root',              
  password : 'root',       
  port: '3306',                   
  database: 'test' 
}); 
 
connection.connect();
 
var delSql = 'DELETE FROM websites where id=6';
//删
connection.query(delSql,function (err, result) {
        if(err){
          console.log('[DELETE ERROR] - ',err.message);
          return;
        }        
 
       console.log('--------------------------DELETE----------------------------');
       console.log('DELETE affectedRows',result.affectedRows);
       console.log('-----------------------------------------------------------------\n\n');  
});
 
connection.end();

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值