天天看點

Node.js快速入門set for nodejsset for nodejs

Node.js快速入門

1、Node.js安裝與配置

1.1 Node.js安裝

(1)源碼編譯安裝

下載下傳最新版源碼:https://nodejs.org/dist/v6.9.5/node-v6.9.5.tar.gz

[[email protected] ~]# cd /usr/local/src/

[[email protected] src]# wget https://nodejs.org/dist/v6.9.5/node-v6.9.5.tar.gz

[[email protected] src]# tar -zxvf node-v6.9.5.tar.gz

[[email protected] src]# cd node-v6.9.5/

[[email protected] 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

[[email protected] node-v6.9.5]# ./configure –prefix=/usr/local/nodejs/6.9.5

creating ./config.gypi

creating ./config.mk

[[email protected] node-v6.9.5]# make

….

rm 9ef67f6c860102054ce727011ffdbf38e27d7d71.intermediate

make[1]: 離開目錄“/usr/local/src/node-v6.9.5/out”

ln -fs out/Release/node node

[[email protected] node-v6.9.5]# make install

(2)Linux二進制包安裝

[[email protected] ~]# wget https://nodejs.org/dist/v6.9.5/node-v6.9.5-linux-x64.tar.xz

[[email protected] ~]# tar -xvf node-v6.9.5-linux-x64.tar.xz -C /opt

注意:沒有用到gzip壓縮去掉z參數

[[email protected] ~]# cd /opt/node-v6.9.5-linux-x64/

[[email protected] node-v6.9.5-linux-x64]# ls

bin CHANGELOG.md include lib LICENSE README.md share

[[email protected] ~]# vi /etc/profile

set for nodejs

export NODE_HOME=/opt/node-v6.9.5-linux-x64

export PATH= NODEHOME/bin: N O D E H O M E / b i n : PATH

[[email protected] ~]# source /etc/profile

[[email protected] ~]# node -v

v6.9.5

[[email protected] ~]# npm -v

3.10.10

1.2 Node.js配置

[[email protected] node-v6.9.5]# vim /etc/profile

set for nodejs

export NODE_HOME=/usr/local/nodejs/6.9.5

export PATH= NODEHOME/bin: N O D E H O M E / b i n : PATH

[[email protected] node-v6.9.5]# source /etc/profile

[[email protected] node-v6.9.5]# node –version

v6.9.5

重新開機系統,再次确認環境變量是否配置成功

[[email protected] ~]# node -v

v6.9.5

2、第一個應用HelloWorld##

2.0 互動式

打開終端,鍵入node進入指令互動模式,可以輸入一條代碼語句後立即執行并顯示結果

[[email protected] hello]# cd

[[email protected] ~]# 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 的檔案,并寫入以下代碼:

[[email protected] ~]# mkdir nodejs

[[email protected] ~]# cd nodejs

[[email protected] nodejs]# mkdir hello

[[email protected] nodejs]# cd hello/

[[email protected] 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/‘);

[[email protected] nodejs]#

以上代碼我們完成了一個可以工作的 HTTP 伺服器。

第一行請求(require)Node.js 自帶的 http 子產品,并且把它指派給 http 變量。

接下來我們調用 http 子產品提供的函數: createServer 。這個函數會傳回 一個對象,這個對象有一個叫做 listen 的方法,這個方法有一個數值參數, 指定這個 HTTP 伺服器監聽的端口号。

使用 node 指令執行以上的代碼:

[[email protected] 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” 來測試是否成功安裝。指令如下,出現版本提示表示安裝成功:

[[email protected] ~]# npm -v

3.10.10

npm安裝路徑是/usr/local/nodejs/6.9.5/lib/node_modules/

[[email protected] ~]# cd /usr/local/nodejs/6.9.5/lib/node_modules/

[[email protected] node_modules]# ls

npm

3.2 更新

如果你安裝的是舊版本的 npm,可以很容易得通過 npm 指令來更新,指令如下:

[[email protected] ~]# npm install npm -g

[[email protected] ~]# 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() 來引入本地安裝的包。

[[email protected] ~]# 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)可以直接在指令行裡使用。

[[email protected] ~]# npm install express -g

/usr/local/nodejs/6.9.5/lib

[[email protected] ~]# cd /usr/local/nodejs/6.9.5/lib/node_modules/

[[email protected] node_modules]# ls

express npm

3.4 使用淘寶 NPM 鏡像

大家都知道國内直接使用 npm 的官方鏡像是非常慢的,這裡推薦使用淘寶 NPM 鏡像。

淘寶 NPM 鏡像是一個完整 npmjs.org 鏡像,你可以用此代替官方版本(隻讀),同步頻率目前為 10分鐘 一次以保證盡量與官方服務同步。

修改源位址為淘寶 NPM 鏡像

npm config set registry http://registry.npm.taobao.org/

[[email protected] ~]# npm config set registry http://registry.npm.taobao.org/

[[email protected] ~]# npm info underscore

可以使用淘寶定制的 cnpm (gzip 壓縮支援) 指令行工具代替預設的 npm:

-g全局安裝目錄:

[[email protected] ~]# 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

[[email protected] node_modules]# pwd

/usr/local/nodejs/6.9.5/lib/node_modules

[[email protected] node_modules]# ls

cnpm npm

這樣就可以使用 cnpm 指令來安裝子產品了:

$ cnpm install [name]

[[email protected] ~]# 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.1[email protected] 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

[[email protected] ~]# npm uninstall express

解除安裝後,你可以到 /node_modules/ 目錄下檢視包是否還存在,或者使用以下指令檢視:

$ npm ls

[[email protected] ~]# npm ls

/root

└── (empty)

3.6 更新子產品

可以使用以下指令更新子產品:

$ npm update express

[[email protected] ~]# npm update express

3.7 搜尋子產品

使用以下來搜尋子產品:

$ npm search express

[[email protected] ~]# 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 建立子產品

[[email protected] hello]# pwd

/root/nodejs/hello

[[email protected] 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 <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: (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)

[[email protected] 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(互動式解釋器)

[[email protected] ~]# 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)

[[email protected] ~]#

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

5、回調函數

Node.js 異步程式設計的直接展現就是回調。

5.1 阻塞代碼執行個體

[[email protected] ~]# cd nodejs/hello/

[[email protected] hello]# vi input.txt

我的部落格:http://blog.csdn.net/chengyuqiang

[[email protected] hello]# vi main.js

var fs = require(“fs”);

var data = fs.readFileSync(‘input.txt’);

console.log(data.toString());

console.log(“程式執行結束!”);

[[email protected] hello]# node main.js

我的部落格:http://blog.csdn.net/chengyuqiang

程式執行結束!

5.2 非阻塞代碼執行個體

[[email protected] 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(“程式執行結束!”);

[[email protected] 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 類來綁定和監聽事件,如下執行個體:

[[email protected] nodejs]# mkdir event

[[email protected] nodejs]# cd event/

[[email protected] 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(“程式執行完畢。”);

[[email protected] event]# node main.js

連接配接成功。

資料接收成功。

程式執行完畢。

在 Node 應用程式中,執行異步操作的函數将回調函數作為最後一個參數, 回調函數接收錯誤對象作為第一個參數。

[[email protected] 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(“程式執行完畢”);

[[email protected] event]# node read.js

程式執行完畢

Error: ENOENT: no such file or directory, open ‘input.txt’

at Error (native)

[[email protected] event]# vi input.txt

hello

[[email protected] 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 屬性用于觸發一個事件。

[[email protected] 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);

[[email protected] event]# node event.js

some_event 事件觸發

注:通過 setTimeout 在 1000 毫秒以後向 event 對象發送事件 some_event

對于每個事件,EventEmitter 支援 若幹個事件監聽器。

當事件觸發時,注冊到這個事件的事件監聽器被依次調用,事件參數作為回調函數參數傳遞。

[[email protected] 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 參數’);

[[email protected] 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 類的應用。

[[email protected] 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(“程式執行完畢。”);

[[email protected] event]# node event3.js

2 個監聽器監聽連接配接事件。

監聽器 listener1 執行。

監聽器 listener2 執行。

listener1 不再受監聽。

監聽器 listener2 執行。

1 個監聽器監聽連接配接事件。

程式執行完畢。

7.3 error 事件

EventEmitter 定義了一個特殊的事件 error,它包含了錯誤的語義,我們在遇到 異常的時候通常會觸發 error 事件。

當 error 被觸發時,EventEmitter 規定如果沒有響 應的監聽器,Node.js 會把它當作異常,退出程式并輸出錯誤資訊。

我們一般要為會觸發 error 事件的對象設定監聽器,避免遇到錯誤後整個程式崩潰。例如:

[[email protected] event]# vi error.js

var events = require(‘events’);

var emitter = new events.EventEmitter();

emitter.emit(‘error’);

[[email protected] 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 堆記憶體之外的一塊原始記憶體。

[[email protected] 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());

[[email protected] 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 - 所有資料已被寫入到底層系統時觸發。

[[email protected] event]# cd ..

[[email protected] nodejs]# mkdir stream

[[email protected] nodejs]# cd stream

9.1 讀流資料

[[email protected] 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(“程式執行完畢”);

[[email protected] stream]# node read.js

程式執行完畢

test

java

c

Hello

9.2 寫流資料

[[email protected] 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(“程式執行完畢”);

[[email protected] stream]# node write.js

程式執行完畢

寫入完成。

[[email protected] stream]# cat output.txt

www.hadron.cn[[email protected] stream]#

9.3 管道流

管道提供了一個輸出流到輸入流的機制。通常我們用于從一個流中擷取資料并将資料傳遞到另外一個流中。

以下執行個體我們通過讀取一個檔案内容并将内容寫入到另外一個檔案中。

[[email protected] 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(“程式執行完畢”);

[[email protected] stream]# node pipe.js

程式執行完畢

[[email protected] stream]# cat output.txt

test

java

c

Hello

9.4 鍊式流

鍊式是通過連接配接輸出流到另外一個流并建立多個對個流操作鍊的機制。鍊式流一般用于管道操作。

接下來我們就是用管道和鍊式來壓縮和解壓檔案。

[[email protected] 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(“檔案壓縮完成。”);

[[email protected] stream]# node compress.js

檔案壓縮完成。

[[email protected] stream]# ls |grep .gz

input.txt.gz

解壓該檔案,建立 decompress.js 檔案

[[email protected] 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(“檔案解壓完成。”);

[[email protected] stream]# node decompress.js

檔案解壓完成。

[[email protected] stream]# cat input2.txt

test

java

c

Hello

10、子產品系統

為了讓Node.js的檔案可以互相調用,Node.js提供了一個簡單的子產品系統。

子產品是Node.js 應用程式的基本組成部分,檔案和子產品是一一對應的。

換言之,一個 Node.js 檔案就是一個子產品,這個檔案可能是JavaScript代碼、JSON或者編譯過的C/C++擴充。

10.1

[[email protected] nodejs]# mkdir module

[[email protected] nodejs]# cd module/

[[email protected] module]# vi hello.js

exports.world = function() {

console.log(‘Hello World’);

}

[[email protected] module]# vi main.js

var hello = require(‘./hello’);

hello.world();

[[email protected] module]# node main.js

Hello World

說明:

hello.js 通過 exports 對象把 world 作為子產品的通路接口,

在 main.js 中通過 require(‘./hello’) 加載這個子產品(./ 為目前目錄,node.js預設字尾為js),然後就可以直接訪 問 hello.js 中 exports 對象的成員函數了。

10.2 有時候我們隻是想把一個對象封裝到子產品中

[[email protected] module]# vi person.js

function Person() {

var name;

this.setName = function(thyName) {

name = thyName;

};

this.sayHello = function() {

console.log(‘Hello ’ + name);

};

};

module.exports = Person;

[[email protected] module]# vi main2.js

var Person = require(‘./person’);

p = new Person();

p.setName(‘Hadron’);

p.sayHello();

[[email protected] 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 參數函數

[[email protected] nodejs]# mkdir fun

[[email protected] nodejs]# cd fun

[[email protected] fun]# vi fun1.js

function say(word) {

console.log(word);

}

function execute(someFunction, value) {

//someFunction相當于C語言的函數指針

someFunction(value);

}

execute(say, “Hello”);

[[email protected] fun]# node fun1.js

Hello

說明:

不需要通過函數指針來了解

把 say 函數作為execute函數的第一個變量進行了傳遞。

say 就變成了execute 中的本地變量 someFunction ,execute可以通過調用 someFunction() (帶括号的形式)來使用 say 函數。

11.2 匿名函數

可以把一個函數作為變量傳遞,但是我們不一定要繞這個”先定義,再傳遞”的圈子,可以直接在另一個函數的括号中定義和傳遞這個函數。

[[email protected] fun]# vi fun2.js

function execute(someFunction, value) {

someFunction(value);

}

execute(function(word){ console.log(word) }, “Hello”);

[[email protected] 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() 被調用或視窗被關閉。

[[email protected] global]# vi main.js

[[email protected] 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

[[email protected] 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 就行了。

[[email protected] global]# vi console.js

console.log();

console.trace();

console.info(“程式開始執行:”);

var counter = 10;

console.log(“計數: %d”, counter);

console.time(“擷取資料”);

//

// 執行一些代碼

//

console.timeEnd(‘擷取資料’);

console.info(“程式執行完畢。”)

[[email protected] global]# node console.js

Trace
    at Object.<anonymous> (/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可能會繼續傳回老的子產品。可以認為,這兩者引用了同一個子產品。

[[email protected] 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(“程式執行結束”);

[[email protected] 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 的功能 過于精簡的不足

[[email protected] nodejs]# mkdir util

[[email protected] nodejs]# cd util

14.1 util.inherits

util.inherits(constructor, superConstructor)是一個實作對象間原型繼承 的函數。

JavaScript 的面向對象特性是基于原型的,與常見的基于類的不同。JavaScript 沒有 提供對象繼承的語言級别特性,而是通過原型複制來實作的。

[[email protected] 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);

[[email protected] 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 方法也不會調用。

[[email protected] 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));

[[email protected] 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。

[[email protected] util]# vi isArray.js

var util = require(‘util’);

console.log(util.isArray([]))

console.log(util.isArray(new Array))

console.log(util.isArray({}))

[[email protected] 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”)

[[email protected] nodejs]# mkdir fs

[[email protected] nodejs]# cd fs

15.1 異步和同步

Node.js 檔案系統(fs 子產品)子產品中的方法均有異步和同步版本,例如讀取檔案内容的函數有異步的 fs.readFile() 和同步的 fs.readFileSync()。

異步的方法函數最後一個參數為回調函數,回調函數的第一個參數包含了錯誤資訊(error)。

建議大家是用異步方法,比起同步,異步方法性能更高,速度更快,而且沒有阻塞。

5.1和5.2分别示範了同步(阻塞)和異步(非阻塞)

[[email protected] fs]# cat input.txt

Hi,node.js

Hi,JavaScript

[[email protected] 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(“程式執行完畢。”);

[[email protected] 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:追加;+:加強

[[email protected] 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(“寫檔案打開成功!”);

});

[[email protected] fs]# node file2.js

準備打開檔案!

檔案打開成功!

寫檔案打開成功!

[[email protected] 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。

[[email protected] 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());

});

[[email protected] 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),在寫入失敗時傳回。

[[email protected] 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());

});

});

[[email protected] 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 為緩沖區對象。

[[email protected] 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());

}

});

});

[[email protected] fs]# node read.js

準備打開已存在的檔案!

檔案打開成功!

準備讀取檔案:

36 位元組被讀取

我是通過寫入的檔案内容!

15.6 關閉檔案

以下為異步模式下關閉檔案的文法格式:

fs.close(fd, callback)

該方法使用了檔案描述符來讀取檔案。

fd - 通過 fs.open() 方法傳回的檔案描述符。

callback - 回調函數,沒有參數。

[[email protected] 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(“檔案關閉成功”);

});

});

});

[[email protected] fs]# node close.js

準備打開檔案!

檔案打開成功!

準備讀取檔案!

我是通過寫入的檔案内容!

檔案關閉成功

15.7 截取檔案

以下為異步模式下截取檔案的文法格式:

fs.ftruncate(fd, len, callback)

該方法使用了檔案描述符來讀取檔案。

參數使用說明如下:

fd - 通過 fs.open() 方法傳回的檔案描述符。

len - 檔案内容截取的長度。

callback - 回調函數,沒有參數。

[[email protected] 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(“檔案關閉成功!”);

});

});

});

});

[[email protected] fs]# node truncate.js

準備打開檔案!

檔案打開成功!

截取10位元組後的檔案内容。

檔案截取成功。

讀取相同的檔案

我是通�

檔案關閉成功!

15.8 删除檔案

以下為删除檔案的文法格式:

fs.unlink(path, callback)

參數使用說明如下:

path - 檔案路徑。

callback - 回調函數,沒有參數。

[[email protected] fs]# vi unlink.js

var fs = require(“fs”);

console.log(“準備删除檔案!”);

fs.unlink(‘input.txt’, function(err) {

if (err) {

return console.error(err);

}

console.log(“檔案删除成功!”);

});

[[email protected] fs]# node unlink.js

準備删除檔案!

檔案删除成功!

[[email protected] 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 - 回調函數,沒有參數。

[[email protected] 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(“目錄建立成功。”);

});

[[email protected] fs]# node mkdir.js

建立目錄 /tmp/test/

目錄建立成功。

[[email protected] fs]# ls /tmp/test/

15.10 讀取目錄

以下為讀取目錄的文法格式:

fs.readdir(path, callback)

參數使用說明如下:

path - 檔案路徑。

callback - 回調函數,回調函數帶有兩個參數err, files,err 為錯誤資訊,files 為 目錄下的檔案數組清單。

[[email protected] 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 );

});

});

[[email protected] 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請求。

[[email protected] nodejs]# mkdir request

[[email protected] nodejs]# cd request/

16.1 擷取GET請求内容

由于GET請求直接被嵌入在路徑中,URL是完整的請求路徑,包括了?後面的部分,是以你可以手動解析後面的内容作為GET請求的參數。

node.js中url子產品中的parse函數提供了這個功能。

[[email protected] 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);

[[email protected] 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 中的參數

[[email protected] 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);

[[email protected] 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

[[email protected] 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);

[[email protected] request]# node server.js

打開下面位址

http://localhost:3000/

顯示輸入框,輸入網站名和網站URL,則以文本形似顯示網站名和網站URL。

17、工具子產品

在 Node.js 子產品庫中有很多好用的子產品。

[[email protected] nodejs]# mkdir md

[[email protected] 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:定義了作業系統的行尾符的常量。

[[email protected] 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.”);

[[email protected] 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 相容的方式互動。

[[email protected] 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’));

[[email protected] 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’ 事件。

[[email protected] 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’);

});

[[email protected] md]# node server.js

server is listening

[[email protected] 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(‘斷開與伺服器的連接配接’);

});

[[email protected] md]# node client.js

連接配接到伺服器!

Hello World!

斷開與伺服器的連接配接

17.4 DNS 子產品

DNS 子產品用于解析域名。引入 DNS 子產品文法格式如下:

var dns = require(“dns”)

[[email protected] 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));

});

});

[[email protected] 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對象

[[email protected] 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(‘轉換為異常,系統将崩潰!’));

[[email protected] 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 - 資料層,一般由資料庫組成。

[[email protected] nodejs]# mkdir web

[[email protected] nodejs]# cd web

18.1 使用 Node 建立 Web 伺服器

Node.js 提供了 http 子產品,http 子產品主要用于搭建 HTTP 服務端和用戶端,使用 HTTP 伺服器或用戶端功能必須調用 http 子產品,代碼如下:

var http = require(‘http’);

以下是示範一個最基本的 HTTP 伺服器架構(使用8081端口),建立 server.js 檔案

[[email protected] web]# vi index.html

Sample Page

Hello World!

[[email protected] 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/‘);

[[email protected] 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 檔案,代碼如下所示:

[[email protected] web]# node server.js

Server running at http://127.0.0.1:8081/

Request for /index.html received.

[[email protected] 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();

[[email protected] web]# node client.js

Sample Page

Hello World!

19、Express 架構

Express 是一個簡潔而靈活的 node.js Web應用架構, 提供了一系列強大特性幫助你建立各種 Web 應用,和豐富的 HTTP 工具。

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

Express 架構核心特性:

可以設定中間件來響應 HTTP 請求。

定義了路由表用于執行不同的 HTTP 請求動作。

可以通過向模闆傳遞參數來動态渲染 HTML 頁面。

19.1 安裝 Express

[[email protected] 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編碼)的表單資料

[[email protected] nodejs]# npm install body-parser –save

[[email protected] nodejs]# npm install cookie-parser –save

[[email protected] nodejs]# npm install multer –save

[[email protected] nodejs]# mkdir express

[[email protected] nodejs]# cd express/

19.2 第一個 Express 架構執行個體

[[email protected] 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)

})

[[email protected] 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 檔案,代碼如下所示:

[[email protected] 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)

})

[[email protected] 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 目錄下放些圖檔,如下所示:

[[email protected] express]# ls img

linux.jpg

[[email protected] 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)

})

[[email protected] 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:

[[email protected] 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)

})

[[email protected] 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 檔案代碼修改如下:

[[email protected] express]# vi portal.html

First Name:

Last Name:

[[email protected] 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)

})

[[email protected] 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 檔案代碼修改如下:

[[email protected] express]# vi upload.html

檔案上傳表單

檔案上傳:

選擇一個檔案上傳:

[[email protected] 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)

})

[[email protected] 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 資料格式。

[[email protected] nodejs]# mkdir rest

[[email protected] 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,内容如下:

[[email protected] 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 檔案代碼如下所示:

[[email protected] 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)

})

[[email protected] 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 檔案代碼如下所示

[[email protected] 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)

})

[[email protected] 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 檔案代碼如下所示:

[[email protected] 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)

})

[[email protected] 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 檔案代碼如下所示:

[[email protected] 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)

})

[[email protected] 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會在父程序與子程序之間,建立一個通信管道,用于程序之間的通信。

[[email protected] nodejs]# mkdir child

[[email protected] 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。

[[email protected] child]# vi support.js

console.log(“程序 ” + process.argv[2] + ” 執行。” );

[[email protected] 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);

});

}

[[email protected] 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

[[email protected] 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);

});

}

[[email protected] 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執行個體的所有方法,還有一個内建的通信信道。

[[email protected] 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);

});

}

[[email protected] child]# node fork.js

程序 0 執行。

程序 1 執行。

程序 2 執行。

子程序已退出,退出碼 0

子程序已退出,退出碼 0

子程序已退出,退出碼 0

22、 JXcore 打包

Node.js 是一個開放源代碼、跨平台的、用于伺服器端和網絡應用的運作環境。

JXcore 是一個支援多線程的 Node.js 發行版本,基本不需要對你現有的代碼做任何改動就可以直接線程安全地以多線程運作。

但我們這篇文章主要是要教大家介紹 JXcore 的打包功能。

繼續閱讀