Node.js快速入门

Node.js快速入门

1、Node.js安装与配置

1.1 Node.js安装

(1)源码编译安装

下载最新版源码:https://nodejs.org/dist/v6.9.5/node-v6.9.5.tar.gz

[root@hadron ~]# cd /usr/local/src/
[root@hadron src]#  wget https://nodejs.org/dist/v6.9.5/node-v6.9.5.tar.gz
[root@hadron src]# tar -zxvf node-v6.9.5.tar.gz     
[root@hadron src]# cd node-v6.9.5/
[root@hadron node-v6.9.5]# ls
android-configure   COLLABORATOR_GUIDE.md  lib         test
AUTHORS             common.gypi            LICENSE     tools
benchmark           configure              Makefile    vcbuild.bat
BSDmakefile         CONTRIBUTING.md        node.gyp    WORKING_GROUPS.md
BUILDING.md         deps                   README.md
CHANGELOG.md        doc                    ROADMAP.md
CODE_OF_CONDUCT.md  GOVERNANCE.md          src
[root@hadron node-v6.9.5]# ./configure --prefix=/usr/local/nodejs/6.9.5
... 
creating ./config.gypi
creating ./config.mk
[root@hadron node-v6.9.5]# make
....
rm 9ef67f6c860102054ce727011ffdbf38e27d7d71.intermediate
make[1]: 离开目录“/usr/local/src/node-v6.9.5/out”
ln -fs out/Release/node node
[root@hadron node-v6.9.5]# make install
(2)Linux二进制包安装
[root@ember ~]# wget https://nodejs.org/dist/v6.9.5/node-v6.9.5-linux-x64.tar.xz
[root@ember ~]# tar -xvf node-v6.9.5-linux-x64.tar.xz -C /opt

注意:没有用到gzip压缩去掉z参数

[root@ember ~]# cd /opt/node-v6.9.5-linux-x64/
[root@ember node-v6.9.5-linux-x64]# ls
bin  CHANGELOG.md  include  lib  LICENSE  README.md  share
[root@ember ~]# vi /etc/profile
#set for nodejs
export NODE_HOME=/opt/node-v6.9.5-linux-x64
export PATH=$NODE_HOME/bin:$PATH
[root@ember ~]# source /etc/profile
[root@ember ~]# node -v
v6.9.5
[root@ember ~]# npm -v
3.10.10

1.2 Node.js配置

[root@hadron node-v6.9.5]# vim /etc/profile
#set for nodejs
export NODE_HOME=/usr/local/nodejs/6.9.5
export PATH=$NODE_HOME/bin:$PATH
[root@hadron node-v6.9.5]# source /etc/profile
[root@hadron node-v6.9.5]# node --version
v6.9.5

重启系统,再次确认环境变量是否配置成功

[root@hadron ~]# node -v
v6.9.5

2、第一个应用HelloWorld##

2.0 交互式

打开终端,键入node进入命令交互模式,可以输入一条代码语句后立即执行并显示结果

[root@hadron hello]# cd
[root@hadron ~]# node
> console.log('Hello World!');
Hello World!
undefined
> 

2.1 NodeJS应用介绍

Node.js 应用是由哪几部分组成的:
1)引入 required 模块:我们可以使用 require 指令来载入 node.js 模块。
2)创建服务器:服务器可以监听客户端的请求,类似于 Apache 、Nginx 等 HTTP 服务器。
3)接收请求与响应请求 服务器很容易创建,客户端可以使用浏览器或终端发送 HTTP 请求,服务器接收请求后返回响应数据。

2.2 创建 Node.js 应用

1)引入 required 模块

我们使用 require 指令来载入 http 模块,并将实例化的 HTTP 赋值给变量 http,实例如下:
var http = require("http");

2)创建服务器

接下来我们使用 http.createServer() 方法创建服务器,并使用 listen 方法绑定 8888 端口。 函数通过 request, response 参数来接收和响应数据。
实例如下,在你项目的根目录下创建一个叫 server.js 的文件,并写入以下代码:

[root@hadron ~]# mkdir nodejs
[root@hadron ~]# cd nodejs
[root@hadron nodejs]# mkdir hello
[root@hadron nodejs]# cd hello/
[root@hadron hello]# vi server.js   
var http = require('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/');
[root@hadron nodejs]#

以上代码我们完成了一个可以工作的 HTTP 服务器。
第一行请求(require)Node.js 自带的 http 模块,并且把它赋值给 http 变量。
接下来我们调用 http 模块提供的函数: createServer 。这个函数会返回 一个对象,这个对象有一个叫做 listen 的方法,这个方法有一个数值参数, 指定这个 HTTP 服务器监听的端口号。

使用 node 命令执行以上的代码:

[root@hadron hello]# node server.js
Server running at http://127.0.0.1:8888/
3)客户端请求

打开浏览器访问 http://127.0.0.1:8888/,你会看到一个写着 “Hello World”的网页。

3、NPM

NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题,常见的使用场景有以下几种:
1)允许用户从NPM服务器下载别人编写的第三方包到本地使用。
2)允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。
3)允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。

3.1 版本

由于新版的nodejs已经集成了npm,可以通过输入 “npm -v” 来测试是否成功安装。命令如下,出现版本提示表示安装成功:

[root@hadron ~]# npm -v
3.10.10

npm安装路径是/usr/local/nodejs/6.9.5/lib/node_modules/

[root@hadron ~]# cd /usr/local/nodejs/6.9.5/lib/node_modules/
[root@hadron node_modules]# ls
npm

3.2 升级

如果你安装的是旧版本的 npm,可以很容易得通过 npm 命令来升级,命令如下:

[root@hadron ~]# npm install npm -g
[root@hadron ~]# npm -v
4.2.0

3.3 使用 npm 命令安装模块

npm 的包安装分为本地安装(local)、全局安装(global)两种,从敲的命令行来看,差别只是有没有-g而已,比如
npm install # 本地安装
npm install -g # 全局安装

3.3.1 本地安装

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

[root@hadron ~]# npm install express
/root
└── [email protected] 
npm WARN enoent ENOENT: no such file or directory, open '/root/package.json'
npm WARN root No description
npm WARN root No repository field.
npm WARN root No README data
npm WARN root No license field.
3.3.2 全局安装

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

[root@hadron ~]# npm install express -g
/usr/local/nodejs/6.9.5/lib
[root@hadron ~]# cd /usr/local/nodejs/6.9.5/lib/node_modules/
[root@hadron node_modules]# ls
express  npm

3.4 使用淘宝 NPM 镜像

大家都知道国内直接使用 npm 的官方镜像是非常慢的,这里推荐使用淘宝 NPM 镜像。
淘宝 NPM 镜像是一个完整 npmjs.org 镜像,你可以用此代替官方版本(只读),同步频率目前为 10分钟 一次以保证尽量与官方服务同步。
修改源地址为淘宝 NPM 镜像
npm config set registry http://registry.npm.taobao.org/

[root@hadron ~]# npm config set registry http://registry.npm.taobao.org/   
[root@hadron ~]# npm info underscore

可以使用淘宝定制的 cnpm (gzip 压缩支持) 命令行工具代替默认的 npm:
-g全局安装目录:

[root@hadron ~]# npm install -g cnpm --registry=https://registry.npm.taobao.org
/usr/local/nodejs/6.9.5/bin/cnpm -> /usr/local/nodejs/6.9.5/lib/node_modules/cnpm/bin/cnpm
/usr/local/nodejs/6.9.5/lib
[root@hadron node_modules]# pwd
/usr/local/nodejs/6.9.5/lib/node_modules
[root@hadron node_modules]# ls
cnpm  npm

这样就可以使用 cnpm 命令来安装模块了:

$ cnpm install [name]
[root@hadron ~]# cnpm install express -g
Downloading express to /usr/local/nodejs/6.9.5/lib/node_modules/express_tmp
Copying /usr/local/nodejs/6.9.5/lib/node_modules/express_tmp/.4.14.1@express to /usr/local/nodejs/6.9.5/lib/node_modules/express
Installing express's dependencies to /usr/local/nodejs/6.9.5/lib/node_modules/express/node_modules

3.5 卸载模块

可以使用以下命令来卸载 Node.js 模块。
$ npm uninstall express

[root@hadron ~]# npm uninstall express

卸载后,你可以到 /node_modules/ 目录下查看包是否还存在,或者使用以下命令查看:
$ npm ls

[root@hadron ~]# npm ls
/root
└── (empty)

3.6 更新模块

可以使用以下命令更新模块:
$ npm update express

[root@hadron ~]# npm update express

3.7 搜索模块

使用以下来搜索模块:
$ npm search express

[root@hadron ~]# npm search express
NAME                      | DESCRIPTION          | AUTHOR          | DATE       
express                   | Fast,…               | =dougwilson…    | 2017-01-28 
express-session           | Simple session…      | =defunctzombie… | 2017-02-11 
morgan                    | HTTP request logger… | =dougwilson     | 2017-02-11 
path-to-regexp            | Express style path…  | =amasad…        | 2016-11-08 
serve-favicon             | favicon serving…     | =dougwilson     | 2016-11-17 
cors                      | middleware for…      | =troygoode…     | 2016-09-08 
csurf                     | CSRF token…          | =defunctzombie… | 2016-05-27 
webpack-hot-middleware    | Webpack hot…         | =glenjamin      | 2017-02-15 
http-proxy-middleware     | The one-liner…       | =chimurai       | 2016-12-07 
express-graphql           | Production ready…    | =leebyron…      | 2017-02-01 
helmet                    | help secure…         | =adam_baldwin…  | 2017-01-13 
express-jwt               | JWT authentication…  | =dschenkelman…  | 2016-10-04 
multer                    | Middleware for…      | =hacksparrow…   | 2017-01-25 
escape-string-regexp      | Escape RegExp…       | =sindresorhus   | 2016-02-21 
express-validator         | Express middleware…  | =ctavan…        | 2016-12-26 
anymatch                  | Matches strings…     | =es128…         | 2015-04-22 
express-generator         | Express'…            | =dougwilson     | 2017-02-02 
ware                      | Easily create your…  | =ianstormtaylor… | 2015-05-01
express-winston           | express.js…          | =bithavoc…      | 2017-02-08 
cookie-session            | cookie session…      | =defunctzombie… | 2017-02-12 

3.8 创建模块

[root@hadron hello]# pwd
/root/nodejs/hello
[root@hadron hello]# 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  --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: (hello) hello
version: (1.0.0) 
description: HelloWorld
entry point: (server.js) 
test command: make test
git repository: 
keywords: 
author: 
license: (ISC) 
About to write to /root/nodejs/hello/package.json:
{
  "name": "hello",
  "version": "1.0.0",
  "description": "HelloWorld",
  "main": "server.js",
  "scripts": {
    "test": "make test",
    "start": "node server.js"
  },
  "author": "",
  "license": "ISC"
}
Is this ok? (yes)
[root@hadron hello]# ls
package.json  server.js

Package.json 属性说明
name - 包名。
version - 包的版本号。
description - 包的描述。
homepage - 包的官网 url 。
author - 包的作者姓名。
contributors - 包的其他贡献者姓名。
dependencies - 依赖包列表。如果依赖包没有安装,npm 会自动将依赖包安装在 node_module 目录下。
repository - 包代码存放的地方的类型,可以是 Git 或 svn,git 可在 Github 上。
main - main 字段是一个模块ID,它是一个指向你程序的主要项目。就是说,如果你包的名字叫 express,然后用户安装它,然后require(“express”)。
keywords - 关键字

4、REPL(交互式解释器)

[root@hadron ~]# node
> 1+1
2
> 5/2
2.5
> (1+2)%2
1
> x=10
10
> var y=20
undefined
> x+y
30
> console.log(x)
10
undefined
> console.log(y)
20
undefined
> > do{
... y++;
... console.log(y);
... }while(y<25)
21
22
23
24
25
undefined

… 三个点的符号是系统自动生成的,你回车换行后即可。Node 会自动检测是否为连续的表达式。
可以使用下划线(_)获取表达式的运算结果

> x+y
35
> var sum=_
undefined
> console.log(sum)
35
undefined
> 
(To exit, press ^C again or type .exit)
> 
[root@hadron ~]# 

ctrl + c 按下两次 - 退出 Node REPL。

5、回调函数

Node.js 异步编程的直接体现就是回调。

5.1 阻塞代码实例

[root@hadron ~]# cd nodejs/hello/
[root@hadron hello]# vi input.txt
我的博客:http://blog.csdn.net/chengyuqiang
[root@hadron hello]# vi main.js
var fs = require("fs");
var data = fs.readFileSync('input.txt');
console.log(data.toString());
console.log("程序执行结束!");
[root@hadron hello]# node main.js
我的博客:http://blog.csdn.net/chengyuqiang
程序执行结束!

5.2 非阻塞代码实例

[root@hadron hello]# vi main2.js
var fs = require("fs");
fs.readFile('input.txt', function (err, data) {
    if (err) return console.error(err);
    console.log(data.toString());
});
console.log("程序执行结束!");
[root@hadron hello]# node main2.js
程序执行结束!
我的博客:http://blog.csdn.net/chengyuqiang

第一个实例在文件读取完后才执行完程序。 第二个实例我们不需要等待文件读取完,这样就可以在读取文件时同时执行接下来的代码,大大提高了程序的性能。因此,阻塞是按顺序执行的,而非阻塞是不需要按顺序的,所以如果需要处理回调函数的参数,我们就需要写在回调函数内。

6、事件循环

Node.js 是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高。
Node.js 的每一个 API 都是异步的,并作为一个独立线程运行,使用异步函数调用,并处理并发。
Node.js 基本上所有的事件机制都是用设计模式中观察者模式实现。
Node.js 单线程类似进入一个while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数.

6.1 事件驱动程序

Node.js 使用事件驱动模型,当web server接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求。
当这个请求完成,它被放回处理队列,当到达队列开头,这个结果被返回给用户。
这个模型非常高效可扩展性非常强,因为webserver一直接受请求而不等待任何读写操作。(这也被称之为非阻塞式IO或者事件驱动IO)
在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数。
整个事件驱动的流程就是这么实现的,非常简洁。有点类似于观察者模式,事件相当于一个主题(Subject),而所有注册到这个事件上的处理函数相当于观察者(Observer)。
Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件,如下实例:

[root@hadron nodejs]# mkdir event
[root@hadron nodejs]# cd event/
[root@hadron event]# vi main.js
// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();
// 创建事件处理程序
var connectHandler = function connected() {
   console.log('连接成功。');
      // 触发 data_received 事件 
   eventEmitter.emit('data_received');
}
// 绑定 connection 事件处理程序
eventEmitter.on('connection', connectHandler);
// 使用匿名函数绑定 data_received 事件
eventEmitter.on('data_received', function(){
   console.log('数据接收成功。');
});
// 触发 connection 事件 
eventEmitter.emit('connection');
console.log("程序执行完毕。");
[root@hadron event]# node main.js
连接成功。
数据接收成功。
程序执行完毕。

在 Node 应用程序中,执行异步操作的函数将回调函数作为最后一个参数, 回调函数接收错误对象作为第一个参数。

[root@hadron event]# vi read.js
var fs = require("fs");
fs.readFile('input.txt', function (err, data) {
   if (err){
      console.log(err.stack);
      return;
   }
   console.log(data.toString());
});
console.log("程序执行完毕");
[root@hadron event]# node read.js
程序执行完毕
Error: ENOENT: no such file or directory, open 'input.txt'
    at Error (native)
[root@hadron event]# vi input.txt
hello
[root@hadron event]# node read.js
程序执行完毕
hello

7、EventEmitter

Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。
Node.js里面的许多对象都会分发事件:一个net.Server对象会在每次有新连接时分发一个事件, 一个fs.readStream对象会在文件被打开的时候发出一个事件。
所有这些产生事件的对象都是 events.EventEmitter 的实例。

7.1 EventEmitter类

events 模块只提供了一个类: events.EventEmitter。EventEmitter 的核心就是事件触发与事件监听器功能的封装。你可以通过require(“events”);来访问该模块。

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

EventEmitter 对象如果在实例化时发生错误,会触发 ‘error’ 事件。当添加新的监听器时,’newListener’ 事件会触发,当监听器被移除时,’removeListener’ 事件被触发。
EventEmitter 提供了多个属性,如 on 和 emit。on 函数用于绑定事件函数,emit 属性用于触发一个事件。

[root@hadron event]# vi event.js
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);
[root@hadron event]# node event.js
some_event 事件触发

注:通过 setTimeout 在 1000 毫秒以后向 event 对象发送事件 some_event
对于每个事件,EventEmitter 支持 若干个事件监听器。
当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。

[root@hadron event]# vi event2.js
var events = require('events');
var emitter = new events.EventEmitter(); 
emitter.on('someEvent', function(arg1, arg2) { 
    console.log('listener1', arg1, arg2); 
}); 
emitter.on('someEvent', function(arg1, arg2) { 
    console.log('listener2', arg1, arg2); 
}); 
emitter.emit('someEvent', 'arg1 参数', 'arg2 参数'); 
[root@hadron event]# node event2.js
listener1 arg1 参数 arg2 参数
listener2 arg1 参数 arg2 参数

7.2 EventEmitter的属性和方法

1)方法:addListener(event, listener)
为指定事件添加一个监听器到监听器数组的尾部。

2)方法:on(event, listener)
为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数。

server.on('connection', function (stream) {
  console.log('someone connected!');
});

3)方法:once(event, listener)
为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器。

server.once('connection', function (stream) {
    console.log('Ah, we have our first user!');
});

4)方法:removeListener(event, listener)
移除指定事件的某个监听器,监听器 必须是该事件已经注册过的监听器。

var callback = function(stream) {
  console.log('someone connected!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);

5)方法:removeAllListeners([event])
移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器。

6)方法:setMaxListeners(n)
默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于提高监听器的默认限制的数量。

7)方法:listeners(event)
返回指定事件的监听器数组。

8)方法:emit(event, [arg1], [arg2], […])
按参数的顺序执行每个监听器,如果事件有注册监听返回 true,否则返回 false。

9)类方法:listenerCount(emitter, event)
返回指定事件的监听器数量。

10)事件:newListener
event - 字符串,事件名称
listener - 处理事件函数
该事件在添加新监听器时被触发。

11)removeListener
event - 字符串,事件名称
listener - 处理事件函数
从指定监听器数组中删除一个监听器。需要注意的是,此操作将会改变处于被删监听器之后的那些监听器的索引。

以下实例通过 connection(连接)事件演示了 EventEmitter 类的应用。

[root@hadron event]# vi event3.js
var events = require('events');
var eventEmitter = new events.EventEmitter();
// 监听器 #1
var listener1 = function listener1() {
   console.log('监听器 listener1 执行。');
}
// 监听器 #2
var listener2 = function listener2() {
  console.log('监听器 listener2 执行。');
}
// 绑定 connection 事件,处理函数为 listener1 
eventEmitter.addListener('connection', listener1);
// 绑定 connection 事件,处理函数为 listener2
eventEmitter.on('connection', listener2);
var eventListeners = require('events').EventEmitter.listenerCount(eventEmitter,'connection');
console.log(eventListeners + " 个监听器监听连接事件。");
// 处理 connection 事件 
eventEmitter.emit('connection');
// 移除监绑定的 listener1 函数
eventEmitter.removeListener('connection', listener1);
console.log("listener1 不再受监听。");
// 触发连接事件
eventEmitter.emit('connection');
eventListeners = require('events').EventEmitter.listenerCount(eventEmitter,'connection');
console.log(eventListeners + " 个监听器监听连接事件。");
console.log("程序执行完毕。");
[root@hadron event]# node event3.js
2 个监听器监听连接事件。
监听器 listener1 执行。
监听器 listener2 执行。
listener1 不再受监听。
监听器 listener2 执行。
1 个监听器监听连接事件。
程序执行完毕。

7.3 error 事件

EventEmitter 定义了一个特殊的事件 error,它包含了错误的语义,我们在遇到 异常的时候通常会触发 error 事件。
当 error 被触发时,EventEmitter 规定如果没有响 应的监听器,Node.js 会把它当作异常,退出程序并输出错误信息。
我们一般要为会触发 error 事件的对象设置监听器,避免遇到错误后整个程序崩溃。例如:

[root@hadron event]# vi error.js
var events = require('events'); 
var emitter = new events.EventEmitter(); 
emitter.emit('error'); 
[root@hadron event]# node error.js
events.js:165
      throw err;
      ^
Error: Uncaught, unspecified "error" event. (undefined)
    at EventEmitter.emit (events.js:163:17)
    at Object. (/root/nodejs/event/error.js:3:9)
    at Module._compile (module.js:570:32)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)
    at Function.Module._load (module.js:438:3)
    at Module.runMain (module.js:604:10)
    at run (bootstrap_node.js:394:7)
    at startup (bootstrap_node.js:149:9)

7.4 继承 EventEmitter

大多数时候我们不会直接使用 EventEmitter,而是在对象中继承它。包括 fs、net、 http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。
为什么要这样做呢?原因有两点:
首先,具有某个实体功能的对象实现事件符合语义, 事件的监听和发射应该是一个对象的方法。
其次 JavaScript 的对象机制是基于原型的,支持 部分多重继承,继承 EventEmitter 不会打乱对象原有的继承关系。

8、Buffer(缓冲区)

javascript 语言自身只有字符串数据类型,没有二进制数据类型。
但在处理像TCP流或文件流时,必须使用到二进制数据。因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。
在 Node.js 中,Buffer 类是随 Node 内核一起发布的核心库。
Buffer 库为 Node.js 带来了一种存储原始数据的方法,可以让 Node.js 处理二进制数据,每当需要在 Node.js 中处理I/O操作中移动的数据时,就有可能使用 Buffer 库。
原始数据存储在 Buffer 类的实例中。一个 Buffer 类似于一个整数数组,但它对应于 V8 堆内存之外的一块原始内存。

[root@hadron event]# vi buffer.js   
//创建长度为 26 字节的 Buffer 实例
var buf1 = new Buffer(26);
//通过给定的数组创建 Buffer 实例
var buf2 = new Buffer([10, 20, 30, 40, 50]);
//通过一个字符串来创建 Buffer 实例
var buf3 = new Buffer("www.hadron.com");

//写入缓冲区
len = buf3.write("test");
console.log("写入字节数:"+len);
console.log("buf3长度:"+buf3.length);

//将Buffer转换为JSON对象
var json = buf2.toJSON(buf2);
console.log(json);

//从缓冲区读取数据
for (var i = 0 ; i < 26 ; i++) {
  buf1[i] = i + 97;
}
console.log( buf1.toString('ascii'));
console.log( buf1.toString('ascii',0,5));
console.log( buf1.toString('utf8',0,5));
console.log( buf1.toString(undefined,0,5));

//缓冲区合并
var buf4 = Buffer.concat([buf1,buf3]);
console.log("buf4内容: " + buf4.toString());

//缓冲区比较
var buffer1 = new Buffer('ABC');
var buffer2 = new Buffer('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 + "之后");
}

//拷贝缓冲区
var buffer3 = new Buffer(3);
buffer1.copy(buffer3);
console.log("buffer3 content: " + buffer3.toString());

//缓冲区裁剪
var buffer4 = buf1.slice(0,9);
console.log("buffer4 content: " + buffer4.toString());

[root@hadron event]# node buffer.js
写入字节数:4
buf3长度:14
{ type: 'Buffer', data: [ 10, 20, 30, 40, 50 ] }
abcdefghijklmnopqrstuvwxyz
abcde
abcde
abcde
buf4内容: abcdefghijklmnopqrstuvwxyztesthadron.com
ABC 在 ABCD之前
buffer3 content: ABC
buffer4 content: abcdefghi

9、Stream(流)

Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对http 服务器发起请求的request 对象就是一个 Stream,还有stdout(标准输出)。
Node.js,Stream 有四种流类型:
Readable - 可读操作。
Writable - 可写操作。
Duplex - 可读可写操作.
Transform - 操作被写入数据,然后读出结果。
所有的 Stream 对象都是 EventEmitter 的实例。常用的事件有:
data - 当有数据可读时触发。
end - 没有更多的数据可读时触发。
error - 在接收和写入过程中发生错误时触发。
finish - 所有数据已被写入到底层系统时触发。

[root@hadron event]# cd ..
[root@hadron nodejs]# mkdir stream
[root@hadron nodejs]# cd stream

9.1 读流数据

[root@hadron stream]# vi read.js
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("程序执行完毕");
[root@hadron stream]# node read.js
程序执行完毕
test
java
c
Hello

9.2 写流数据

[root@hadron stream]# vi write.js
var fs = require("fs");
var data ='www.hadron.cn';
// 创建一个可以写入的流,写入到文件 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("程序执行完毕");
[root@hadron stream]# node write.js
程序执行完毕
写入完成。
[root@hadron stream]# cat output.txt 
www.hadron.cn[root@hadron stream]# 

9.3 管道流

管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中。
以下实例我们通过读取一个文件内容并将内容写入到另外一个文件中。

[root@hadron stream]# vi pipe.js
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("程序执行完毕");
[root@hadron stream]# node pipe.js
程序执行完毕
[root@hadron stream]# cat output.txt 
test
java
c
Hello

9.4 链式流

链式是通过连接输出流到另外一个流并创建多个对个流操作链的机制。链式流一般用于管道操作。
接下来我们就是用管道和链式来压缩和解压文件。

[root@hadron stream]# vi compress.js 
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("文件压缩完成。");
[root@hadron stream]# node compress.js 
文件压缩完成。
[root@hadron stream]# ls |grep .gz
input.txt.gz

解压该文件,创建 decompress.js 文件

[root@hadron stream]# vi decompress.js  
var fs = require("fs");
var zlib = require('zlib');
// 解压 input.txt.gz 文件为 input.txt
fs.createReadStream('input.txt.gz')
  .pipe(zlib.createGunzip())
  .pipe(fs.createWriteStream('input2.txt'));
console.log("文件解压完成。");
[root@hadron stream]# node decompress.js 
文件解压完成。
[root@hadron stream]# cat input2.txt 
test
java
c
Hello

10、模块系统

为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统。
模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的。
换言之,一个 Node.js 文件就是一个模块,这个文件可能是JavaScript代码、JSON或者编译过的C/C++扩展。

10.1

[root@hadron nodejs]# mkdir module
[root@hadron nodejs]# cd module/
[root@hadron module]# vi hello.js
exports.world = function() {
  console.log('Hello World');
}
[root@hadron module]# vi main.js
var hello = require('./hello');
hello.world();
[root@hadron module]# node main.js
Hello World

说明:
hello.js 通过 exports 对象把 world 作为模块的访问接口,
在 main.js 中通过 require(‘./hello’) 加载这个模块(./ 为当前目录,node.js默认后缀为js),然后就可以直接访 问 hello.js 中 exports 对象的成员函数了。

10.2 有时候我们只是想把一个对象封装到模块中

[root@hadron module]# vi person.js
function Person() {
    var name;
    this.setName = function(thyName) {
            name = thyName;
    };
    this.sayHello = function() {
            console.log('Hello ' + name);
    };
};
module.exports = Person;
[root@hadron module]# vi main2.js
var Person = require('./person');
p = new Person();
p.setName('Hadron');
p.sayHello();
[root@hadron module]# node main2.js
Hello Hadron

说明:
模块接口的唯一变化是使用 module.exports = Person 代替了exports.world = function(){}。
在外部引用该模块时,其接口对象就是要输出的 Person 对象本身,而不是原先的 exports。

10.3 服务端的模块放在哪里

Node.js中存在4类模块(原生模块和3种文件模块)
1)原生模块的优先级仅次于文件模块缓存的优先级,优先加载文件模块的缓存中已经存在的模块。
2)require方法在解析文件名之后,优先检查模块是否在原生模块列表中。
以http模块为例,尽管在目录下存在一个http/http.js/http.node/http.json文件,require(“http”)都不会从这些文件中加载,而是从原生模块中加载。
原生模块也有一个缓存区,同样也是优先从缓存区加载。如果缓存区没有被加载过,则调用原生模块的加载方式进行加载和执行。
3)当文件模块缓存中不存在,而且不是原生模块的时候,Node.js会解析require方法传入的参数,并从文件系统中加载实际的文件

require方法接受以下几种参数的传递:
1)http、fs、path等,原生模块。
2)./mod或../mod,相对路径的文件模块。
3)/pathtomodule/mod,绝对路径的文件模块。
4)mod,非原生模块的文件模块。

11、函数

在JavaScript中,一个函数可以作为另一个函数接收一个参数。我们可以先定义一个函数,然后传递,也可以在传递参数的地方直接定义函数。
Node.js中函数的使用与Javascript类似

11.1 参数函数

[root@hadron nodejs]# mkdir fun
[root@hadron nodejs]# cd fun
[root@hadron fun]# vi fun1.js
function say(word) {
  console.log(word);
}
function execute(someFunction, value) {
   //someFunction相当于C语言的函数指针
  someFunction(value);
}
execute(say, "Hello");
[root@hadron fun]# node fun1.js
Hello

说明:
不需要通过函数指针来理解
把 say 函数作为execute函数的第一个变量进行了传递。
say 就变成了execute 中的本地变量 someFunction ,execute可以通过调用 someFunction() (带括号的形式)来使用 say 函数。

11.2 匿名函数

可以把一个函数作为变量传递,但是我们不一定要绕这个”先定义,再传递”的圈子,可以直接在另一个函数的括号中定义和传递这个函数。

[root@hadron fun]# vi fun2.js
function execute(someFunction, value) {
  someFunction(value);
}
execute(function(word){ console.log(word) }, "Hello");
[root@hadron fun]# node fun2.js
Hello

11.3 函数传递是如何让HTTP服务器工作的

带着这些知识,我们再来看看我们简约而不简单的HTTP服务器:

var http = require("http");
http.createServer(function(request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("Hello World");
  response.end();
}).listen(8888);

现在它看上去应该清晰了很多:我们向 createServer 函数传递了一个匿名函数。
用这样的代码也可以达到同样的目的:

var http = require("http");
function onRequest(request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("Hello World");
  response.end();
}
http.createServer(onRequest).listen(8888);

12、 路由

客户端提供请求的URL和其他需要的GET及POST参数,随后服务器端根据这些数据来执行相应的代码。
我们需要查看HTTP请求,从中提取出请求的URL以及GET/POST参数。这一功能应当属于路由,而不是服务器

13、全局对象

在浏览器 JavaScript 中,通常 window 是全局对象, 而 Node.js 中的全局对象是 global,所有全局变量(除了 global 本身以外)都是 global 对象的属性。
在 Node.js 我们可以直接访问到 global 的属性,而不需要在应用中包含它。
global 最根本的作用是作为全局变量的宿主。
满足以下条 件的变量是全局变量:
1)在最外层定义的变量;
2)全局对象的属性;
3)隐式定义的变量(未定义直接赋值的变量)。
注意: 永远使用 var 定义变量以避免引入全局变量,因为全局变量会污染 命名空间,提高代码的耦合风险。

13.1 __filename

__filename 表示当前正在执行的脚本的文件名。它将输出文件所在位置的绝对路径,且和命令行参数所指定的文件名不一定相同。
如果在模块中,返回的值是模块文件的路径。

13.2 __dirname

__dirname 表示当前执行脚本所在的目录。

13.3 setTimeout(cb, ms)

setTimeout(cb, ms) 全局函数在指定的毫秒(ms)数后执行指定函数(cb)。:setTimeout() 只执行一次指定函数。
返回一个代表定时器的句柄值。

13.4 clearTimeout(t)

clearTimeout( t ) 全局函数用于停止一个之前通过 setTimeout() 创建的定时器。 参数 t 是通过 setTimeout() 函数创建的定时器。

13.5 setInterval(cb, ms)

setInterval(cb, ms) 全局函数在指定的毫秒(ms)数后执行指定函数(cb)。
返回一个代表定时器的句柄值。可以使用 clearInterval(t) 函数来清除定时器。
setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭。

 [root@hadron global]# vi main.js
 [root@hadron global]# node main.js
 /root/nodejs/global/main.js
 /root/nodejs/global
 1:Hello, World!
 2:Hello, World!
 2:Hello, World!
 2:Hello, World!
 ^C
 [root@hadron global]#

以上程序每隔两秒就会输出一次”Hello, World!”,且会永久执行下去,直到你按下 ctrl + c 按钮

13.6 console

console 用于提供控制台标准输出,它是由 Internet Explorer 的 JScript 引擎提供的调试工具,后来逐渐成为览器的事实标准。
Node.js 沿用了这个标准,提供与习惯行为一致的 console 对象,用于向标准输出流(stdout)或标准错误流(stderr)输出字符。

(1)console.log()

向标准输出流打印字符并以换行符结束。
console.log 接受若干 个参数,如果只有一个参数,则输出这个参数的字符串形式。如果有多个参数,则 以类似于C 语言 printf() 命令的格式输出。
第一个参数是一个字符串,如果没有 参数,只打印一个换行。

(2)console.info([data][, …])

该命令的作用是返回信息性消息,这个命令与console.log差别并不大

(3)console.time(label)

输出时间,表示计时开始。

(4)console.timeEnd(label)

结束时间,表示计时结束。

(5)console.trace(message[, …])

当前执行的代码在堆栈中的调用路径,这个测试函数运行很有帮助,只要给想测试的函数里面加入 console.trace 就行了。

 [root@hadron global]# vi console.js
    console.log();
    console.trace();
    console.info("程序开始执行:");
    var counter = 10;
    console.log("计数: %d", counter);
    console.time("获取数据");
    //
    // 执行一些代码
    //
    console.timeEnd('获取数据');
    console.info("程序执行完毕。")
    [root@hadron global]# node console.js 

    Trace
        at Object. (/root/nodejs/global/console.js:2:9)
        at Module._compile (module.js:570:32)
        at Object.Module._extensions..js (module.js:579:10)
        at Module.load (module.js:487:32)
        at tryModuleLoad (module.js:446:12)
        at Function.Module._load (module.js:438:3)
        at Module.runMain (module.js:604:10)
        at run (bootstrap_node.js:394:7)
        at startup (bootstrap_node.js:149:9)
        at bootstrap_node.js:509:3
    程序开始执行:
    计数: 10
    获取数据: 0.021ms
    程序执行完毕。

13.7 process

process 是一个全局变量,即 global 对象的属性。
它用于描述当前Node.js 进程状态的对象,提供了一个与操作系统的简单接口。通常在你写本地命令行程序的时候,少不了要 和它打交道。

1) process 对象的一些最常用的成员方法

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

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

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

[root@hadron global]# vi process.js
process.on('exit', function(code) {
    // 以下代码永远不会执行
  setTimeout(function() {
    console.log("该代码不会执行");
    }, 0);
  console.log('退出码为:', code);
});
// 输出到终端
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);
// 输出当前目录
console.log('当前目录: ' + process.cwd());
// 输出当前版本
console.log('当前版本: ' + process.version);
// 输出内存使用情况
console.log(process.memoryUsage());
console.log("程序执行结束");
[root@hadron global]# node process.js
Hello World!
0: /usr/local/nodejs/6.9.5/bin/node
1: /root/nodejs/global/process.js
/usr/local/nodejs/6.9.5/bin/node
linux
当前目录: /root/nodejs/global
当前版本: v6.9.5
{ rss: 16932864, heapTotal: 10522624, heapUsed: 4213712 }
程序执行结束
退出码为: 0

14、 常用工具

util 是一个Node.js 核心模块,提供常用函数的集合,用于弥补核心JavaScript 的功能 过于精简的不足

[root@hadron nodejs]# mkdir util
[root@hadron nodejs]# cd util

14.1 util.inherits

util.inherits(constructor, superConstructor)是一个实现对象间原型继承 的函数。
JavaScript 的面向对象特性是基于原型的,与常见的基于类的不同。JavaScript 没有 提供对象继承的语言级别特性,而是通过原型复制来实现的。

[root@hadron util]# vi inherits.js
var util = require('util');
function Base() {
    this.name = 'base';
    this.base = 1991;
    this.sayHello = function() {
            console.log('Hello ' + this.name);
    };
}
Base.prototype.showName = function() {
    console.log(this.name);
};
function Sub() {
    this.name = 'sub';
}
util.inherits(Sub, Base);
var objBase = new Base();
objBase.showName();
objBase.sayHello();
console.log(objBase);
var objSub = new Sub();
objSub.showName();
//objSub.sayHello();
console.log(objSub);
[root@hadron util]# node inherits.js
base
Hello base
Base { name: 'base', base: 1991, sayHello: [Function] }
sub
Sub { name: 'sub' }
说明:

定义了一个基础对象Base 和一个继承自Base 的Sub,Base 有三个在构造函数 内定义的属性和一个原型中定义的函数,通过util.inherits 实现继承。
Sub 仅仅继承了Base 在原型中定义的函数,而构造函数内部创造的 base 属 性和 sayHello 函数都没有被 Sub 继承。
同时,在原型中定义的属性不会被console.log 作 为对象的属性输出。如果我们去掉 objSub.sayHello(); 这行的注释,将报错。

14.2 util.inspect

util.inspect(object,[showHidden],[depth],[colors])是一个将任意对象转换 为字符串的方法,通常用于调试和错误输出。
它至少接受一个参数 object,即要转换的对象。showHidden 是一个可选参数,如果值为 true,将会输出更多隐藏信息。
depth 表示最大递归的层数,如果对象很复杂,你可以指定层数以控制输出信息的多 少。如果不指定depth,默认会递归2层,指定为 null 表示将不限递归层数完整遍历对象。
如果color 值为 true,输出格式将会以ANSI 颜色编码,通常用于在终端显示更漂亮 的效果。
特别要指出的是,util.inspect 并不会简单地直接把对象转换为字符串,即使该对 象定义了toString 方法也不会调用。

[root@hadron util]# vi inspect.js
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)); 
[root@hadron util]# node inspect.js
Person { name: 'byvoid', toString: [Function] }
Person {
  name: 'byvoid',
  toString: 
   { [Function]
     [length]: 0,
     [name]: '',
     [arguments]: null,
     [caller]: null,
     [prototype]: { [constructor]: [Circular] } } }

14.3 util.isArray(object)

如果给定的参数 “object” 是一个数组返回true,否则返回false。

[root@hadron util]# vi isArray.js
var util = require('util');
console.log(util.isArray([]))
console.log(util.isArray(new Array))
console.log(util.isArray({}))
[root@hadron util]# node isArray.js
true
true
false

14.4 util.isRegExp(object)

如果给定的参数 “object” 是一个正则表达式返回true,否则返回false。

var util = require('util');
util.isRegExp(/some regexp/)
  // true
util.isRegExp(new RegExp('another regexp'))
  // true
util.isRegExp({})
  // false

14.5 util.isDate(object)

如果给定的参数 “object” 是一个日期返回true,否则返回false。

var util = require('util');
util.isDate(new Date())
  // true
util.isDate(Date())
  // false (without 'new' returns a String)
util.isDate({})
  // false

14.6 util.isError(object)

如果给定的参数 “object” 是一个错误对象返回true,否则返回false。

var util = require('util');
util.isError(new Error())
// true
util.isError(new TypeError())
// true
util.isError({ name: 'Error', message: 'an error occurred' })
// false

15、文件系统

Node.js 提供一组类似 UNIX(POSIX)标准的文件操作API。 Node 导入文件系统模块(fs)语法如下所示:
var fs = require(“fs”)

[root@hadron nodejs]# mkdir fs
[root@hadron nodejs]# cd fs

15.1 异步和同步

Node.js 文件系统(fs 模块)模块中的方法均有异步和同步版本,例如读取文件内容的函数有异步的 fs.readFile() 和同步的 fs.readFileSync()。
异步的方法函数最后一个参数为回调函数,回调函数的第一个参数包含了错误信息(error)。
建议大家是用异步方法,比起同步,异步方法性能更高,速度更快,而且没有阻塞。
5.1和5.2分别演示了同步(阻塞)和异步(非阻塞)

[root@hadron fs]# cat input.txt 
Hi,node.js
Hi,JavaScript
[root@hadron fs]# vi file.js
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("程序执行完毕。");
[root@hadron fs]# node file.js
同步读取: Hi,node.js
Hi,JavaScript

程序执行完毕。
异步读取: Hi,node.js
Hi,JavaScript

15.2 打开文件

fs.open(path, flags[, mode], callback)
path - 文件的路径。
flags - 文件打开的行为。具体值详见下文。
mode - 设置文件模式(权限),文件创建默认权限为 0666(可读,可写)。
callback - 回调函数,带有两个参数如:callback(err, fd)。

flags 参数可以是以下值:
r:读;w:写;s:同步;a:追加;+:加强

[root@hadron fs]# vi file2.js
var fs = require("fs");
// 异步打开文件
console.log("准备打开文件!");
fs.open('input.txt', 'r+', function(err, fd) {
   if (err) {
       return console.error(err);
   }
  console.log("文件打开成功!");
});
    //写文件,如果不存在则创建文件
fs.open('test.txt','w',function(err,fd){
  if(err){
    return console.error(err);
   }
   console.log("写文件打开成功!");
});
[root@hadron fs]# node file2.js
准备打开文件!
文件打开成功!
写文件打开成功!
[root@hadron fs]# ls
file2.js  file.js  input.txt  test.txt

15.3 获取文件信息

fs.stat(path, callback)
path - 文件路径。
callback - 回调函数,带有两个参数如:(err, stats), stats 是 fs.Stats 对象。
fs.stat(path)执行后,会将stats类的实例返回给其回调函数。可以通过stats类中的提供方法判断文件的相关属性。例如判断是否为文件:

var fs = require('fs');
fs.stat('/Users/liuht/code/itbilu/demo/fs.js', function (err, stats) {
    console.log(stats.isFile());        //true
})
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。

[root@hadron fs]# vi file3.js
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());    
});
[root@hadron fs]# node file3.js
准备打开文件!
{ dev: 2050,
  mode: 33188,
  nlink: 1,
  uid: 0,
  gid: 0,
  rdev: 0,
  blksize: 4096,
  ino: 58327978,
  size: 25,
  blocks: 8,
  atime: 2017-02-21T01:22:00.883Z,
  mtime: 2017-02-21T01:21:56.513Z,
  ctime: 2017-02-21T01:21:56.513Z,
  birthtime: 2017-02-21T01:21:56.513Z }
读取文件信息成功!
是否为文件(isFile) ? true
是否为目录(isDirectory) ? false

15.4 写入文件

以下为异步模式下写入文件的语法格式:
fs.writeFile(filename, data[, options], callback)
如果文件存在,该方法写入的内容会覆盖旧的文件内容。
path - 文件路径。
data - 要写入文件的数据,可以是 String(字符串) 或 Buffer(流) 对象。
options - 该参数是一个对象,包含 {encoding, mode, flag}。默认编码为 utf8, 模式为 0666 , flag 为 ‘w’
callback - 回调函数,回调函数只包含错误信息参数(err),在写入失败时返回。

[root@hadron fs]# vi write.js
var fs = require("fs");
console.log("准备写入文件");
fs.writeFile('input.txt', '我是通过写入的文件内容!',  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());
   });
});
[root@hadron fs]# node write.js 
准备写入文件
数据写入成功!
--------我是分割线-------------
读取写入的数据!
异步读取文件数据: 我是通过写入的文件内容!

注:fs.readFile(filename, [options], callback),不需要fs.open

15.5 读取文件

以下为异步模式下读取文件的语法格式:
fs.read(fd, buffer, offset, length, position, callback)
该方法使用了文件描述符来读取文件。
fd - 通过 fs.open() 方法返回的文件描述符。
buffer - 数据写入的缓冲区。
offset - 缓冲区写入的写入偏移量。
length - 要从文件中读取的字节数。
position - 文件读取的起始位置,如果 position 的值为 null,则会从当前文件指针的位置读取。
callback - 回调函数,有三个参数err, bytesRead, buffer,err 为错误信息, bytesRead 表示读取的字节数,buffer 为缓冲区对象。

[root@hadron fs]# vi read.js
var fs = require("fs");
var buf = new Buffer(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());
      }
   });
});
[root@hadron fs]# node read.js 
准备打开已存在的文件!
文件打开成功!
准备读取文件:
36  字节被读取
我是通过写入的文件内容!

15.6 关闭文件

以下为异步模式下关闭文件的语法格式:
fs.close(fd, callback)
该方法使用了文件描述符来读取文件。
fd - 通过 fs.open() 方法返回的文件描述符。
callback - 回调函数,没有参数。

[root@hadron fs]# vi close.js
var fs = require("fs");
var buf = new Buffer(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("文件关闭成功");
      });
   });
});
[root@hadron fs]# node close.js 
准备打开文件!
文件打开成功!
准备读取文件!
我是通过写入的文件内容!
文件关闭成功

15.7 截取文件

以下为异步模式下截取文件的语法格式:
fs.ftruncate(fd, len, callback)
该方法使用了文件描述符来读取文件。
参数使用说明如下:
fd - 通过 fs.open() 方法返回的文件描述符。
len - 文件内容截取的长度。
callback - 回调函数,没有参数。

[root@hadron fs]# vi truncate.js
var fs = require("fs");
var buf = new Buffer(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("文件关闭成功!");
     });
      });
   });
});
[root@hadron fs]# node truncate.js
准备打开文件!
文件打开成功!
截取10字节后的文件内容。
文件截取成功。
读取相同的文件
我是通�
文件关闭成功!

15.8 删除文件

以下为删除文件的语法格式:
fs.unlink(path, callback)
参数使用说明如下:
path - 文件路径。
callback - 回调函数,没有参数。

[root@hadron fs]# vi unlink.js
var fs = require("fs");
console.log("准备删除文件!");
fs.unlink('input.txt', function(err) {
   if (err) {
       return console.error(err);
      }
   console.log("文件删除成功!");
});
[root@hadron fs]# node unlink.js
准备删除文件!
文件删除成功!
[root@hadron fs]# ls
close.js  file2.js  file3.js  file.js  read.js  test.txt  truncate.js  unlink.js  write.js

15.9 创建目录

以下为创建目录的语法格式:
fs.mkdir(path[, mode], callback)
参数使用说明如下:
path - 文件路径。
mode - 设置目录权限,默认为 0777。
callback - 回调函数,没有参数。

[root@hadron fs]# vi mkdir.js
var fs = require("fs");
console.log("创建目录 /tmp/test/");
fs.mkdir("/tmp/test/",function(err){
   if (err) {
       return console.error(err);
   }
   console.log("目录创建成功。");
});
[root@hadron fs]# node mkdir.js
创建目录 /tmp/test/
目录创建成功。
[root@hadron fs]# ls /tmp/test/

15.10 读取目录

以下为读取目录的语法格式:
fs.readdir(path, callback)
参数使用说明如下:
path - 文件路径。
callback - 回调函数,回调函数带有两个参数err, files,err 为错误信息,files 为 目录下的文件数组列表。

[root@hadron fs]# vi readdir.js
var fs = require("fs");
console.log("查看/root/nodejs/fs目录");
fs.readdir("/root/nodejs/fs",function(err, files){
   if (err) {
       return console.error(err);
   }
   files.forEach( function (file){
       console.log( file );
   });
});
[root@hadron fs]# node readdir.js
查看 /tmp 目录
close.js
file.js
file2.js
file3.js
mkdir.js
read.js
readdir.js
test.txt
truncate.js
unlink.js
write.js

16、GET/POST请求

在很多场景中,我们的服务器都需要跟用户的浏览器打交道,如表单提交。
表单提交到服务器一般都使用GET/POST请求。
本章节我们将为大家介绍 Node.js GET/POST请求。

[root@hadron nodejs]# mkdir request
[root@hadron nodejs]# cd request/

16.1 获取GET请求内容

由于GET请求直接被嵌入在路径中,URL是完整的请求路径,包括了?后面的部分,因此你可以手动解析后面的内容作为GET请求的参数。
node.js中url模块中的parse函数提供了这个功能。

[root@hadron request]# vi server.js
var http = require('http');
var url = require('url');
var util = require('util');

http.createServer(function(req, res){
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end(util.inspect(url.parse(req.url, true)));
}).listen(3000);
[root@hadron request]# node server.js
等待状态

打开下面地址
http://localhost:3000/user?name=hadron&url=www.hadron.com
页面输出:
Url {
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: ‘?name=hadron&url=www.hadron.com’,
query: { name: ‘hadron’, url: ‘www.hadron.com’ },
pathname: ‘/user’,
path: ‘/user?name=hadron&url=www.hadron.com’,
href: ‘/user?name=hadron&url=www.hadron.com’ }

16.2 获取 URL 的参数

可以使用 url.parse 方法来解析 URL 中的参数

[root@hadron request]# vi server.js
var http = require('http');
var url = require('url');
var util = require('util');
http.createServer(function(req, res){
    res.writeHead(200, {'Content-Type': 'text/html; charset=utf8'});
       // 解析 url 参数
    var params = url.parse(req.url, true).query;
    res.write("网站名:" + params.name);
    res.write("
"); res.write("网站 URL:" + params.url); res.end(); }).listen(3000); [root@hadron request]# node server.js 等待状态

打开下面地址
http://localhost:3000/user?name=hadron&url=www.hadron.com
页面输出:
网站名:hadron
网站 URL:www.hadron.com

16.3 获取 POST 请求内容

POST 请求的内容全部的都在请求体中,http.ServerRequest 并没有一个属性内容为请求体,原因是等待请求体传输可能是一件耗时的工作。
比如上传文件,而很多时候我们可能并不需要理会请求体的内容,恶意的POST请求会大大消耗服务器的资源,所有node.js 默认是不会解析请求体的,当你需要的时候,需要手动来做。

基本语法结构说明

var http = require('http');
var querystring = require('querystring');
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);

server.js

[root@hadron request]# vi server.js
var http = require('http');
var querystring = require('querystring');
var postHTML = 
  ' Node.js 实例' +
  '' +
  '
' + '网站名:
' + '网站 URL:
' + '' + '
' + ''; 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("
"); res.write("网站 URL:" + body.url); } else { // 输出表单 res.write(postHTML); } res.end(); }); }).listen(3000); [root@hadron request]# node server.js

打开下面地址
http://localhost:3000/
显示输入框,输入网站名和网站URL,则以文本形似显示网站名和网站URL。

17、工具模块

在 Node.js 模块库中有很多好用的模块。

[root@hadron nodejs]# mkdir md
[root@hadron nodejs]# cd md

17.1 OS 模块

提供基本的系统操作函数。
可以通过以下方式引入该模块:
var os = require(“os”)
方法 & 描述:
1)os.tmpdir()返回操作系统的默认临时文件夹。
2)os.endianness()返回 CPU 的字节序,可能的是 “BE” 或 “LE”。
3)os.hostname()返回操作系统的主机名。
4)os.type()返回操作系统名
5)os.platform()返回操作系统名
6)os.arch()返回操作系统 CPU 架构,可能的值有 “x64”、”arm” 和 “ia32”。
7)os.release()返回操作系统的发行版本。
8)os.uptime()返回操作系统运行的时间,以秒为单位。
9)os.loadavg()返回一个包含 1、5、15 分钟平均负载的数组。
10)os.totalmem()返回系统内存总量,单位为字节。
11)os.freemem()返回操作系统空闲内存量,单位是字节。
12) os.cpus()返回一个对象数组,包含所安装的每个 CPU/内核的信息:型号、速度(单位 MHz)、
时间(一个包含 user、nice、sys、idle 和 irq 所使用 CPU/内核毫秒数的对象)。
13)os.networkInterfaces()获得网络接口列表。

属性os.EOL:定义了操作系统的行尾符的常量。

[root@hadron md]# vi os.js
var os = require("os");
// CPU 的字节序
console.log('endianness : ' + os.endianness());
// 操作系统名
console.log('type : ' + os.type());
// 操作系统名
console.log('platform : ' + os.platform());
// 系统内存总量
console.log('total memory : ' + os.totalmem() + " bytes.");
// 操作系统空闲内存量
console.log('free memory : ' + os.freemem() + " bytes.");
[root@hadron md]# node os.js
endianness : LE
type : Linux
platform : linux
total memory : 16539013120 bytes.
free memory : 8061796352 bytes.

17.2 Path 模块

path 模块提供了一些用于处理文件路径的小工具,我们可以通过以下方式引入该模块:
var path = require(“path”)

序号 方法 & 描述
1)path.normalize(p)规范化路径,注意’..’ 和 ‘.’。
2)path.join([path1][, path2][, …])用于连接路径。该方法的主要用途在于,会正确使用当前系统的路径分隔符,Unix系统是”/”,Windows系统是”\”。
3)path.resolve([from …], to)将 to 参数解析为绝对路径。
4)path.isAbsolute(path)判断参数 path 是否是绝对路径。
5)path.relative(from, to)用于将相对路径转为绝对路径。
6)path.dirname(p)返回路径中代表文件夹的部分,同 Unix 的dirname 命令类似。
7)path.basename(p[, ext])返回路径中的最后一部分。同 Unix 命令 bashname 类似。
8)path.extname(p)返回路径中文件的后缀名,即路径中最后一个’.’之后的部分。
如果一个路径中并不包含’.’或该路径只包含一个’.’ 且这个’.’为路径的第一个字符,则此命令返回空字符串。
9)path.parse(pathString)返回路径字符串的对象。
10)path.format(pathObject)从对象中返回路径字符串,和 path.parse 相反。

序号 属性 & 描述
1)path.sep平台的文件路径分隔符,’\’ 或 ‘/’。
2)path.delimiter平台的分隔符, ; or ‘:’.
3)path.posix提供上述 path 的方法,不过总是以 posix 兼容的方式交互。
4)path.win32提供上述 path 的方法,不过总是以 win32 兼容的方式交互。

[root@hadron md]# vi path.js
var path = require("path");
// 格式化路径
console.log('normalization : ' + path.normalize('/test/test1//2slashes/1slash/tab/..'));
// 连接路径
console.log('joint path : ' + path.join('/test', 'test1', '2slashes/1slash', 'tab', '..'));
// 转换为绝对路径
console.log('resolve : ' + path.resolve('main.js'));
// 路径中文件的后缀名
console.log('ext name : ' + path.extname('main.js'));
[root@hadron md]# node path.js
normalization : /test/test1/2slashes/1slash
joint path : /test/test1/2slashes/1slash
resolve : /root/nodejs/md/main.js
ext name : .js

17.3 Net 模块

Net 模块提供了一些用于底层的网络通信的小工具,包含了创建服务器/客户端的方法,我们可以通过以下方式引入该模块:
var net = require(“net”)
1)net.createServer([options][, connectionListener])
创建一个 TCP 服务器。参数 connectionListener 自动给 ‘connection’ 事件创建监听器。
2)net.connect(options[, connectionListener])
返回一个新的 ‘net.Socket’,并连接到指定的地址和端口。
当 socket 建立的时候,将会触发 ‘connect’ 事件。

[root@hadron md]# vi server.js
var net = require('net');
var server = net.createServer(function(connection) {
   console.log('client connected');
   connection.on('end', function() {
      console.log('客户端关闭连接');
     });
   connection.write('Hello World!\r\n');
   connection.pipe(connection);
});
server.listen(8080, function() {
  console.log('server is listening');
});
[root@hadron md]# node server.js
server is listening

[root@hadron md]# vi client.js
var net = require('net');
var client = net.connect({port: 8080}, function() {
   console.log('连接到服务器!');
});
client.on('data', function(data) {
   console.log(data.toString());
   client.end();
});
client.on('end', function() {
   console.log('断开与服务器的连接');
});

[root@hadron md]# node client.js 
连接到服务器!
Hello World!
断开与服务器的连接

17.4 DNS 模块

DNS 模块用于解析域名。引入 DNS 模块语法格式如下:
var dns = require(“dns”)

[root@hadron md]# vi dns.js
var dns = require('dns');
dns.lookup('www.github.com', function onLookup(err, address, family) {
   console.log('ip 地址:', address);
   dns.reverse(address, function (err, hostnames) {
    if (err) {
            console.log(err.stack);
     }
     console.log('反向解析 ' + address + ': ' + JSON.stringify(hostnames));
     });  
});
[root@hadron md]# node dns.js
ip 地址: 115.239.211.112
Error: getHostByAddr ENOTFOUND 115.239.211.112
    at errnoException (dns.js:28:10)
    at QueryReqWrap.onresolve [as oncomplete] (dns.js:216:19)
反向解析 115.239.211.112: undefined

注:程序有问题

17.5 Domain 模块

Domain(域) 简化异步代码的异常处理,可以捕捉处理try catch无法捕捉的异常。引入 Domain 模块 语法格式如下:
var domain = require(“domain”)
domain模块,把处理多个不同的IO的操作作为一个组。注册事件和回调到domain,当发生一个错误事件或抛出一个错误时,domain对象会被通知,不会丢失上下文环境,也不导致程序错误立即推出,与process.on(‘uncaughtException’)不同。
Domain 模块可分为隐式绑定和显式绑定:
1)隐式绑定: 把在domain上下文中定义的变量,自动绑定到domain对象
2)显式绑定: 把不是在domain上下文中定义的变量,以代码的方式绑定到domain对象

[root@hadron md]# vi domain.js
var EventEmitter = require("events").EventEmitter;
var domain = require("domain");
var emitter1 = new EventEmitter();
// 创建域
var domain1 = domain.create();
domain1.on('error', function(err){
   console.log("domain1 处理这个错误 ("+err.message+")");
});
// 显式绑定
domain1.add(emitter1);
emitter1.on('error',function(err){
   console.log("监听器处理此错误 ("+err.message+")");
});
emitter1.emit('error',new Error('通过监听器来处理'));
emitter1.removeAllListeners('error');
emitter1.emit('error',new Error('通过 domain1 处理'));
var domain2 = domain.create();
domain2.on('error', function(err){
   console.log("domain2 处理这个错误 ("+err.message+")");
});
// 隐式绑定
domain2.run(function(){
   var emitter2 = new EventEmitter();
   emitter2.emit('error',new Error('通过 domain2 处理'));   
});
domain1.remove(emitter1);
emitter1.emit('error', new Error('转换为异常,系统将崩溃!'));
[root@hadron md]# node domain.js
监听器处理此错误 (通过监听器来处理)
domain1 处理这个错误 (通过 domain1 处理)
domain2 处理这个错误 (通过 domain2 处理)
events.js:160
      throw er; // Unhandled 'error' event
      ^
Error: 转换为异常,系统将崩溃!
    at Object. (/root/nodejs/md/domain.js:42:24)
    at Module._compile (module.js:570:32)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)
    at Function.Module._load (module.js:438:3)
    at Module.runMain (module.js:604:10)
    at run (bootstrap_node.js:394:7)
    at startup (bootstrap_node.js:149:9)
    at bootstrap_node.js:509:3

18、Web 模块

Web服务器一般指网站服务器,是指驻留于因特网上某种类型计算机的程序,Web服务器的基本功能就是提供Web信息浏览服务。它只需支持HTTP协议、HTML文档格式及URL,与客户端的网络浏览器配合。
大多数 web 服务器都支持服务端的脚本语言(PHP、Python、ruby)等,并通过脚本语言从数据库获取数据,将结果返回给客户端浏览器。
目前最主流的三个Web服务器是Apache、Nginx、IIS。
Client - 客户端,一般指浏览器,浏览器可以通过 HTTP 协议向服务器请求数据。
Server - 服务端,一般指 Web 服务器,可以接收客户端请求,并向客户端发送响应数据。
Business - 业务层, 通过 Web 服务器处理应用程序,如与数据库交互,逻辑运算,调用外部程序等。
Data - 数据层,一般由数据库组成。

[root@hadron nodejs]# mkdir web
[root@hadron nodejs]# cd web

18.1 使用 Node 创建 Web 服务器

Node.js 提供了 http 模块,http 模块主要用于搭建 HTTP 服务端和客户端,使用 HTTP 服务器或客户端功能必须调用 http 模块,代码如下:
var http = require('http');
以下是演示一个最基本的 HTTP 服务器架构(使用8081端口),创建 server.js 文件

[root@hadron web]# vi index.html


Sample Page


Hello World!


[root@hadron web]# vi server.js
    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/plain
            response.writeHead(404, {'Content-Type': 'text/html'});
          }else{             
            // HTTP 状态码: 200 : OK
            // Content Type: text/plain
            response.writeHead(200, {'Content-Type': 'text/html'}); 
            // 响应文件内容
            response.write(data.toString());        
            }
            //  发送响应数据
        response.end();
       });   
    }).listen(8081);
    // 控制台会输出以下信息
    console.log('Server running at http://127.0.0.1:8081/');
[root@hadron web]# node server.js
Server running at http://127.0.0.1:8081/
Request for /index.html received.

18.2 使用 Node 创建 Web 客户端

Node 创建 Web 客户端需要引入 http 模块,创建 client.js 文件,代码如下所示:

[root@hadron web]# node server.js
Server running at http://127.0.0.1:8081/
Request for /index.html received.

[root@hadron web]# vi client.js 
var http = require('http');
// 用于请求的选项
var options = {
   host: 'localhost',
   port: '8081',
   path: '/index.htm'  
 };
// 处理响应的回调函数
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();
[root@hadron web]# node client.js 


Sample Page


Hello World!


19、Express 框架

Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。
使用 Express 可以快速地搭建一个完整功能的网站。
Express 框架核心特性:
可以设置中间件来响应 HTTP 请求。
定义了路由表用于执行不同的 HTTP 请求动作。
可以通过向模板传递参数来动态渲染 HTML 页面。

19.1 安装 Express

[root@hadron nodejs]# 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编码)的表单数据

[root@hadron nodejs]# npm install body-parser --save
[root@hadron nodejs]# npm install cookie-parser --save
[root@hadron nodejs]# npm install multer --save
[root@hadron nodejs]# mkdir express
[root@hadron nodejs]# cd express/

19.2 第一个 Express 框架实例

[root@hadron express]# vi xp1.js
var express = require('express');
var app = express();
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)
})
[root@hadron express]# node xp1.js 
应用实例,访问地址为 http://:::8081

在浏览器中访问 http://127.0.0.1:8081,输出Hello, World

19.3 请求和响应

Express 应用使用回调函数的参数: request 和 response 对象来处理请求和响应的数据。
app.get(‘/’, function (req, res) {
// –
})
request 和 response 对象的具体介绍:

1) Request 对象

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

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

2) Response 对象

response 对象表示 HTTP 响应,即在接收到请求时向客户端发送的 HTTP 响应数据。常见属性有:
res.app:同req.app一样
res.append():追加指定HTTP头
res.set()在res.append()后将重置之前设置的头
res.cookie(name,value [,option]):设置Cookie
opition: domain / expires / httpOnly / maxAge / path / secure / signed
res.clearCookie():清除Cookie
res.download():传送指定路径的文件
res.get():返回指定的HTTP头
res.json():传送JSON响应
res.jsonp():传送JSONP响应
res.location():只设置响应的Location HTTP头,不设置状态码或者close response
res.redirect():设置响应的Location HTTP头,并且设置状态码302
res.send():传送HTTP响应
res.sendFile(path [,options] [,fn]):传送指定路径的文件 -会自动根据文件extension设定Content-Type
res.set():设置HTTP头,传入object可以一次设置多个头
res.status():设置HTTP状态码
res.type():设置Content-Type的MIME类型

19.4 路由

我们已经了解了 HTTP 请求的基本应用,而路由决定了由谁(指定脚本)去响应客户端请求。
在HTTP请求中,我们可以通过路由提取出请求的URL以及GET/POST参数。
接下来我们扩展 Hello World,添加一些功能来处理更多类型的 HTTP 请求。
创建 xp2.js 文件,代码如下所示:

[root@hadron express]# vi xp2.js
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)
})
[root@hadron express]# node xp2.js
应用实例,访问地址为 http://:::8081
主页 GET 请求
/list_user GET 请求
/ab*cd GET 请求

http://127.0.0.1:8081/
Hello GET
http://127.0.0.1:8081/list_user
用户列表页面
http://127.0.0.1:8081/abcd
正则匹配
http://127.0.0.1:8081/abcdefg
Cannot GET /abcdefg

19.5 静态文件

Express 提供了内置的中间件 express.static 来设置静态文件如:图片, CSS, JavaScript 等。
你可以使用 express.static 中间件来设置静态文件路径。例如,如果你将图片, CSS, JavaScript 文件放在 public 目录下,你可以这么写:
app.use(express.static(‘public’));
我们可以到 public/images 目录下放些图片,如下所示:

[root@hadron express]# ls img
linux.jpg
[root@hadron express]# vi xp3.js
var express = require('express');
var app = express();
app.use(express.static('img'));
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)
})
[root@hadron express]# node xp3.js 
应用实例,访问地址为 http://:::8081

通过地址http://127.0.0.1:8081/linux.jpg即可访问图片

19.6 GET 方法

以下实例演示了在表单中通过 GET 方法提交两个参数,我们可以使用 server.js 文件内的 process_get 路由器来处理输入:
index.html 文件代码如下:



First Name:
Last Name:
[root@hadron express]# vi server.js var express = require('express'); var app = express(); app.use(express.static('img')); app.get('/index.html', function (req, res) { res.sendFile( __dirname + "/" + "index.html" ); }) app.get('/process_get', function (req, res) { // 输出 JSON 格式 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) }) [root@hadron express]# node server.js 应用实例,访问地址为 http://:::8081

浏览器:
http://127.0.0.1:8081/index.html
http://127.0.0.1:8081/process_get?first_name=a&last_name=b
{“first_name”:”a”,”last_name”:”b”}

19.7 POST 方法

以下实例演示了在表单中通过 POST 方法提交两个参数,我们可以使用 server.js 文件内的 process_post 路由器来处理输入:
index.htm 文件代码修改如下:

[root@hadron express]# vi portal.html


First Name:
Last Name:
[root@hadron express]# vi post.js var express = require('express'); var app = express(); var bodyParser = require('body-parser'); // 创建 application/x-www-form-urlencoded 编码解析 var urlencodedParser = bodyParser.urlencoded({ extended: false }) app.use(express.static('public')); app.get('/portal.html', function (req, res) { res.sendFile( __dirname + "/" + "portal.html" ); }) app.post('/process_post', urlencodedParser, function (req, res) { // 输出 JSON 格式 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) }) [root@hadron express]# node post.js 应用实例,访问地址为 http://:::8081 { first_name: 'a', last_name: 'b' }

http://127.0.0.1:8081/portal.html
输入a和b
http://127.0.0.1:8081/process_post
{“first_name”:”a”,”last_name”:”b”}

19.8 文件上传

以下我们创建一个用于上传文件的表单,使用 POST 方法,表单 enctype 属性设置为 multipart/form-data。
index.htm 文件代码修改如下:

[root@hadron express]# vi upload.html


文件上传表单


文件上传:

选择一个文件上传:

[root@hadron express]# vi upload.js var express = require('express'); var app = express(); var fs = require("fs"); var bodyParser = require('body-parser'); var multer = require('multer'); app.use(express.static('public')); app.use(bodyParser.urlencoded({ extended: false })); app.use(multer({ dest: '/tmp/'}).array('image')); app.get('/upload.html', function (req, res) { res.sendFile( __dirname + "/" + "upload.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) }) [root@hadron express]# node upload.js 应用实例,访问地址为 http://:::8081

http://127.0.0.1:8081/upload.html
http://127.0.0.1:8081/file_upload
{“message”:”File uploaded successfully”,”filename”:”tmp.txt”}

20、RESTful API

20.1 什么是 REST?

REST即表述性状态传递(英文:Representational State Transfer,简称REST)是Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格。
表述性状态转移是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是RESTful。需要注意的是,REST是设计风格而不是标准。
REST通常基于使用HTTP,URI,和XML(标准通用标记语言下的一个子集)以及HTML(标准通用标记语言下的一个应用)这些现有的广泛流行的协议和标准。
REST 通常使用 JSON 数据格式。

[root@hadron nodejs]# mkdir rest
[root@hadron nodejs]# cd rest

20.2 HTTP 方法

以下为 REST 基本架构的四个方法:
GET - 用于获取数据。
PUT - 用于添加数据。
DELETE - 用于删除数据。
POST - 用于更新或添加数据。

20.3 RESTful Web Services

Web service是一个平台独立的,低耦合的,自包含的、基于可编程的web的应用程序,可使用开放的XML(标准通用标记语言下的一个子集)标准来描述、发布、发现、协调和配置这些应用程序,用于开发分布式的互操作的应用程序。
基于 REST 架构的 Web Services 即是 RESTful。
由于轻量级以及通过 HTTP 直接传输数据的特性,Web 服务的 RESTful 方法已经成为最常见的替代方法。可以使用各种语言(比如 Java 程序、Perl、Ruby、python、php 和 Javascript[包括 Ajax])实现客户端。
RESTful Web 服务通常可以通过自动客户端或代表用户的应用程序访问。但是,这种服务的简便性让用户能够与之直接交互,使用它们的 Web 浏览器构建一个 GET URL 并读取返回的内容。更多介绍,可以查看:RESTful 架构详解http://www.runoob.com/w3cnote/restful-architecture.html

20.4 创建 RESTful

首先,创建一个 json 数据资源文件 users.json,内容如下:

[root@hadron rest]# vi 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
   }
}

基于以上数据,我们创建以下 RESTful API:
序号 URI HTTP方法 发送内容 结果
1 listUsers GET 空 显示所有用户列表
2 addUser POST JSON字符串 添加新用户
3 deleteUser DELETE JSON字符串 删除用户
4 :id GET 空 显示用户详细信息
获取用户列表:
以下代码,我们创建了 RESTful API listUsers,用于读取用户的信息列表, server.js 文件代码如下所示:

[root@hadron rest]# vi list.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)
})
[root@hadron rest]# node list.js
应用实例,访问地址为 http://:::8081

浏览器打开http://127.0.0.1:8081/listUsers,显示如下:
{
“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
}
}

20.5 添加用户

以下代码,我们创建了 RESTful API addUser, 用于添加新的用户数据,add.js 文件代码如下所示

[root@hadron rest]# vi add.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)
})
[root@hadron rest]# node add.js
应用实例,访问地址为 http://:::8081

http://127.0.0.1:8081/addUser
{“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},
“user4”:{“name”:”mohit”,”password”:”password4”,”profession”:”teacher”,”id”:4}}

20.6 显示用户详情

以下代码,我们创建了 RESTful API :id(用户id), 用于读取指定用户的详细信息,detail.js 文件代码如下所示:

[root@hadron rest]# vi detail.js    
var express = require('express');
var app = express();
var fs = require("fs");
app.get('/:id', function (req, res) {
   // 首先我们读取已存在的用户
   fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {
       data = JSON.parse( data );
       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)

})
[root@hadron rest]# node detail.js
应用实例,访问地址为 http://:::8081
{ name: 'suresh',
  password: 'password2',
  profession: 'librarian',
  id: 2 }

http://127.0.0.1:8081/2
{“name”:”suresh”,”password”:”password2”,”profession”:”librarian”,”id”:2}

20.7 删除用户

以下代码,我们创建了 RESTful API deleteUser, 用于删除指定用户的详细信息,以下实例中,用户 id 为 2,server.js 文件代码如下所示:

[root@hadron rest]# vi delete.js
var express = require('express');
var app = express();
var fs = require("fs");
var id = 2;
app.get('/deleteUser', function (req, res) {
   // First read existing users.
   fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {
       data = JSON.parse( data );
       delete data["user" + 2];     
       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)
})
[root@hadron rest]# node delete.js
应用实例,访问地址为 http://:::8081
{ user1: 
   { name: 'mahesh',
     password: 'password1',
     profession: 'teacher',
     id: 1 },
  user3: 
   { name: 'ramesh',
     password: 'password3',
     profession: 'clerk',
     id: 3 } }

http://127.0.0.1:8081/deleteUser
{“user1”:{“name”:”mahesh”,”password”:”password1”,”profession”:”teacher”,”id”:1},
“user3”:{“name”:”ramesh”,”password”:”password3”,”profession”:”clerk”,”id”:3}}

21、多进程

我们都知道 Node.js 是以单线程的模式运行的,但它使用的是事件驱动来处理并发,这样有助于我们在多核 cpu 的系统上创建多个子进程,从而提高性能。
每个子进程总是带有三个流对象:child.stdin, child.stdout 和child.stderr。他们可能会共享父进程的 stdio 流,或者也可以是独立的被导流的流对象。
Node 提供了 child_process 模块来创建子进程,方法有:
exec - child_process.exec 使用子进程执行命令,缓存子进程的输出,并将子进程的输出以回调函数参数的形式返回。
spawn - child_process.spawn 使用指定的命令行参数创建新进程。
fork - child_process.fork 是 spawn()的特殊形式,用于在子进程中运行的模块,如 fork(‘./son.js’) 相当于 spawn(‘node’, [‘./son.js’]) 。
与spawn方法不同的是,fork会在父进程与子进程之间,建立一个通信管道,用于进程之间的通信。

[root@hadron nodejs]# mkdir child
[root@hadron nodejs]# cd child/

21.1 exec() 方法

child_process.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() 方法返回最大的缓冲区,并等待进程结束,一次性返回缓冲区的内容。

实例
让我们创建两个 js 文件 support.js 和 master.js。

[root@hadron child]# vi support.js
console.log("进程 " + process.argv[2] + " 执行。" );

[root@hadron child]# vi 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);
      });
}
[root@hadron child]# node master.js 
子进程已退出,退出码 0
stdout: 进程 0 执行。

stderr: 
子进程已退出,退出码 0
子进程已退出,退出码 0
stdout: 进程 2 执行。

stderr: 
stdout: 进程 1 执行。

stderr: 

21.2 spawn() 方法

child_process.spawn 使用指定的命令行参数创建新进程,语法格式如下:
child_process.spawn(command[, args][, options])
参数说明如下:
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() 就开始接收响应。

support.js 文件代码:

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

spawn.js

[root@hadron child]# vi spawn.js
const fs = require('fs');
const child_process = require('child_process');
for(var i=0; i<3; i++) {
   var workerProcess = child_process.spawn('node', ['support.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);
      });
}
[root@hadron child]# node spawn.js
stdout: 进程 0 执行。

stdout: 进程 1 执行。

stdout: 进程 2 执行。

子进程已退出,退出码 0
子进程已退出,退出码 0
子进程已退出,退出码 0

21.3 fork 方法

child_process.fork 是 spawn() 方法的特殊形式,用于创建进程,语法格式如下:
child_process.fork(modulePath[, args][, options])
参数说明如下:
modulePath: String,将要在子进程中运行的模块
args: Array 字符串参数数组
options:Object
cwd String 子进程的当前工作目录
env Object 环境变量键值对
execPath String 创建子进程的可执行文件
execArgv Array 子进程的可执行文件的字符串参数数组(默认: process.execArgv)
silent Boolean 如果为true,子进程的stdin,stdout和stderr将会被关联至父进程,否则,它们将会从父进程中继承。(默认为:false)
uid Number 设置用户进程的 ID
gid Number 设置进程组的 ID
返回的对象除了拥有ChildProcess实例的所有方法,还有一个内建的通信信道。

[root@hadron child]# vi fork.js
const fs = require('fs');
const child_process = require('child_process');
for(var i=0; i<3; i++) {
   var worker_process = child_process.fork("support.js", [i]);
   worker_process.on('close', function (code) {
      console.log('子进程已退出,退出码 ' + code);
   });
}
[root@hadron child]# node fork.js
进程 0 执行。
进程 1 执行。
进程 2 执行。
子进程已退出,退出码 0
子进程已退出,退出码 0
子进程已退出,退出码 0

22、 JXcore 打包

Node.js 是一个开放源代码、跨平台的、用于服务器端和网络应用的运行环境。
JXcore 是一个支持多线程的 Node.js 发行版本,基本不需要对你现有的代码做任何改动就可以直接线程安全地以多线程运行。
但我们这篇文章主要是要教大家介绍 JXcore 的打包功能。

你可能感兴趣的:(Node.js,node,node.js,快速入门)