柚子快報激活碼778899分享:Node.js---菜鳥教程
柚子快報激活碼778899分享:Node.js---菜鳥教程
文章目錄
創(chuàng)建第一個應(yīng)用創(chuàng)建 Node.js 應(yīng)用
NPM 使用介紹使用 npm 命令安裝模塊本地安裝使用 package.json模塊的操作
回調(diào)函數(shù)阻塞代碼實例非阻塞代碼
事件循環(huán)事件驅(qū)動程序
EventEmitterEventEmitter 類方法實例error 事件繼承 EventEmitter
Buffer(緩沖區(qū))Buffer 與字符編碼創(chuàng)建 Buffer 類寫入緩沖區(qū)從緩沖區(qū)讀取數(shù)據(jù)將 Buffer 轉(zhuǎn)換為 JSON 對象緩沖區(qū)合并緩沖區(qū)比較拷貝緩沖區(qū)緩沖區(qū)裁剪緩沖區(qū)長度
Stream(流)從流中讀取數(shù)據(jù)寫入流管道流鏈?zhǔn)搅?/p>
模塊系統(tǒng)引入模塊
函數(shù)匿名函數(shù)函數(shù)傳遞是如何讓 HTTP 服務(wù)器工作的
路由全局對象__filename__dirnamesetTimeout(cb, ms)clearTimeout(t)setInterval(cb, ms)consoleprocessProcess 屬性方法參考手冊
常用工具util.callbackifyutil.inheritsutil.inspectutil.isArrayutil.isRegExp(object)util.isDate(object)
文件系統(tǒng)異步和同步打開文件獲取文件信息寫入文件讀取文件關(guān)閉文件截取文件刪除文件創(chuàng)建目錄讀取目錄刪除目錄
GET/POST 請求獲取 GET 請求內(nèi)容獲取 POST 請求內(nèi)容
Web 模塊Web 應(yīng)用架構(gòu)使用 Node 創(chuàng)建 Web 服務(wù)器使用 Node 創(chuàng)建 Web 客戶端
Express 框架安裝第一個 Express 框架實例請求和響應(yīng)路由靜態(tài)文件GET 方法POST 方法文件上傳Cookie 管理
RESTful API創(chuàng)建 RESTful獲取用戶列表:添加用戶顯示用戶詳情刪除用戶
多進程exec() 方法
創(chuàng)建第一個應(yīng)用
讓我們先了解下 Node.js 應(yīng)用是由哪幾部分組成的:
require 指令:在 Node.js 中,使用 require 指令來加載和引入模塊,引入的模塊可以是內(nèi)置模塊,也可以是第三方模塊或自定義模塊。 創(chuàng)建服務(wù)器:服務(wù)器可以監(jiān)聽客戶端的請求,類似于 Apache 、Nginx 等 HTTP 服務(wù)器。 接收請求與響應(yīng)請求: 服務(wù)器很容易創(chuàng)建,客戶端可以使用瀏覽器或終端發(fā)送 HTTP 請求,服務(wù)器接收請求后返回響應(yīng)數(shù)據(jù)。
創(chuàng)建 Node.js 應(yīng)用
步驟一、使用 require 指令來加載和引入模塊
const module = require('module-name');
其中,module-name 可以是一個文件路徑(相對或絕對路徑),也可以是一個模塊名稱,如果是一個模塊名稱,Node.js 會自動從 node_modules 目錄中查找該模塊。
我們使用 require 指令來載入 http 模塊,并將實例化的 HTTP 賦值給變量 http,實例如下:
var http = require("http");
步驟二、創(chuàng)建服務(wù)器 接下來我們使用 http.createServer() 方法創(chuàng)建服務(wù)器,并使用 listen 方法綁定 8888 端口。 函數(shù)通過 request, response 參數(shù)來接收和響應(yīng)數(shù)據(jù)。
實例如下,在你項目的根目錄下創(chuàng)建一個叫 server.js 的文件,并寫入以下代碼:
var http = require('http');
http.createServer(function (request, response) {
// 發(fā)送 HTTP 頭部
// HTTP 狀態(tài)值: 200 : OK
// 內(nèi)容類型: text/plain
response.writeHead(200, {'Content-Type': 'text/plain'});
// 發(fā)送響應(yīng)數(shù)據(jù) "Hello World"
response.end('Hello World\n');
}).listen(8888);
// 終端打印如下信息
console.log('Server running at http://127.0.0.1:8888/');
以上代碼我們完成了一個可以工作的 HTTP 服務(wù)器。
使用 node 命令執(zhí)行以上的代碼:
node server.js
接下來,打開瀏覽器訪問 http://127.0.0.1:8888/,你會看到一個寫著 "Hello World"的網(wǎng)頁。 分析Node.js 的 HTTP 服務(wù)器:
第一行請求(require)Node.js 自帶的 http 模塊,并且把它賦值給 http 變量。接下來我們調(diào)用 http 模塊提供的函數(shù): createServer 。這個函數(shù)會返回 一個對象,這個對象有一個叫做 listen 的方法,這個方法有一個數(shù)值參數(shù), 指定這個 HTTP 服務(wù)器監(jiān)聽的端口號。
NPM 使用介紹
NPM是隨同NodeJS一起安裝的包管理工具,能解決NodeJS代碼部署上的很多問題。
可以通過輸入 “npm -v” 來測試是否成功安裝。
使用 npm 命令安裝模塊
以下實例,我們使用 npm 命令安裝常用的 Node.js web框架模塊 express:
npm install express
安裝好之后,express 包就放在了工程目錄下的 node_modules 目錄中,因此在代碼中只需要通過 require(‘express’) 的方式就好,無需指定第三方包路徑。
var express = require('express');
本地安裝
將安裝包放在 ./node_modules 下(運行 npm 命令時所在的目錄),如果沒有 node_modules 目錄,會在當(dāng)前執(zhí)行 npm 命令的目錄下生成 node_modules 目錄??梢酝ㄟ^ require() 來引入本地安裝的包。
使用 package.json
package.json 位于模塊的目錄下,用于定義包的屬性。
模塊的操作
卸載模塊
npm uninstall express
卸載后,你可以到 /node_modules/ 目錄下查看包是否還存在,或者使用以下命令查看:
npm ls
更新模塊
npm update express
搜索模塊
npm search express
回調(diào)函數(shù)
Node.js 異步編程的直接體現(xiàn)就是回調(diào)。
異步編程依托于回調(diào)來實現(xiàn),但不能說使用了回調(diào)后程序就異步化了。
回調(diào)函數(shù)在完成任務(wù)后就會被調(diào)用,Node 使用了大量的回調(diào)函數(shù),Node 所有 API 都支持回調(diào)函數(shù)。
例如,我們可以一邊讀取文件,一邊執(zhí)行其他命令,在文件讀取完成后,我們將文件內(nèi)容作為回調(diào)函數(shù)的參數(shù)返回。這樣在執(zhí)行代碼時就沒有阻塞或等待文件 I/O 操作。這就大大提高了 Node.js 的性能,可以處理大量的并發(fā)請求。
回調(diào)函數(shù)一般作為函數(shù)的最后一個參數(shù)出現(xiàn):
function foo1(name, age, callback) { }
function foo2(value, callback1, callback2) { }
阻塞代碼實例
創(chuàng)建一個文件 input.txt ,內(nèi)容如下:
菜鳥教程官網(wǎng)地址:www.runoob.com
創(chuàng)建 main.js 文件, 代碼如下:
var fs = require("fs");
var data = fs.readFileSync('input.txt');
console.log(data.toString());
console.log("程序執(zhí)行結(jié)束!");
以上代碼執(zhí)行結(jié)果如下:
非阻塞代碼
創(chuàng)建 main.js 文件, 代碼如下:
var fs = require("fs");
fs.readFile('input.txt', function (err, data) {
if (err) return console.error(err);
console.log(data.toString());
});
console.log("程序執(zhí)行結(jié)束!");
以上兩個實例我們了解了阻塞與非阻塞調(diào)用的不同。第一個實例在文件讀取完后才執(zhí)行程序。 第二個實例我們不需要等待文件讀取完,這樣就可以在讀取文件時同時執(zhí)行接下來的代碼,大大提高了程序的性能。
因此,阻塞是按順序執(zhí)行的,而非阻塞是不需要按順序的,所以如果需要處理回調(diào)函數(shù)的參數(shù),我們就需要寫在回調(diào)函數(shù)內(nèi)。
事件循環(huán)
事件驅(qū)動程序
Node.js 使用事件驅(qū)動模型,當(dāng)web server接收到請求,就把它關(guān)閉然后進行處理,然后去服務(wù)下一個web請求。
當(dāng)這個請求完成,它被放回處理隊列,當(dāng)?shù)竭_隊列開頭,這個結(jié)果被返回給用戶。
這個模型非常高效可擴展性非常強,因為 webserver 一直接受請求而不等待任何讀寫操作。
在事件驅(qū)動模型中,會生成一個主循環(huán)來監(jiān)聽事件,當(dāng)檢測到事件時觸發(fā)回調(diào)函數(shù)。
Node.js 有多個內(nèi)置的事件,我們可以通過引入 events 模塊,并通過實例化 EventEmitter 類來綁定和監(jiān)聽事件,如下實例:
創(chuàng)建 main.js 文件,代碼如下所示:
// 引入 events 模塊
var events = require('events');
// 創(chuàng)建 eventEmitter 對象
var eventEmitter = new events.EventEmitter();
// 創(chuàng)建事件處理程序
var connectHandler = function connected() {
console.log('連接成功。');
// 觸發(fā) data_received 事件
eventEmitter.emit('data_received');
}
// 綁定 connection 事件處理程序
eventEmitter.on('connection', connectHandler);
// 使用匿名函數(shù)綁定 data_received 事件
eventEmitter.on('data_received', function(){
console.log('數(shù)據(jù)接收成功。');
});
// 觸發(fā) connection 事件
eventEmitter.emit('connection');
console.log("程序執(zhí)行完畢。");
EventEmitter
Node.js 所有的異步 I/O 操作在完成時都會發(fā)送一個事件到事件隊列。
EventEmitter 類
events 模塊只提供了一個對象: events.EventEmitter。EventEmitter 的核心就是事件觸發(fā)與事件監(jiān)聽器功能的封裝。
你可以通過require(“events”);來訪問該模塊。
// 引入 events 模塊
var events = require('events');
// 創(chuàng)建 eventEmitter 對象
var eventEmitter = new events.EventEmitter();
下面我們用一個簡單的例子說明 EventEmitter 的用法:
//event.js 文件
var EventEmitter = require('events').EventEmitter;
var event = new EventEmitter();
event.on('some_event', function() {
console.log('some_event 事件觸發(fā)');
});
setTimeout(function() {
event.emit('some_event');
}, 1000);
運行這段代碼,1 秒后控制臺輸出了 ‘some_event 事件觸發(fā)’。
其原理是 event 對象注冊了事件 some_event 的一個監(jiān)聽器,然后我們通過 setTimeout 在 1000 毫秒以后向 event 對象發(fā)送事件 some_event,此時會調(diào)用some_event 的監(jiān)聽器。
EventEmitter 的每個事件由一個事件名和若干個參數(shù)組成,事件名是一個字符串,通常表達一定的語義。對于每個事件,EventEmitter 支持 若干個事件監(jiān)聽器。
當(dāng)事件觸發(fā)時,注冊到這個事件的事件監(jiān)聽器被依次調(diào)用,事件參數(shù)作為回調(diào)函數(shù)參數(shù)傳遞。
讓我們以下面的例子解釋這個過程:
//event.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 參數(shù)', 'arg2 參數(shù)');
以上例子中,emitter 為事件 someEvent 注冊了兩個事件監(jiān)聽器,然后觸發(fā)了 someEvent 事件。
運行結(jié)果中可以看到兩個事件監(jiān)聽器回調(diào)函數(shù)被先后調(diào)用。 這就是EventEmitter最簡單的用法。
EventEmitter 提供了多個屬性,如 on 和 emit。on 函數(shù)用于綁定事件函數(shù),emit 屬性用于觸發(fā)一個事件。接下來我們來具體看下 EventEmitter 的屬性介紹。
方法
addListener(event, listener) 為指定事件添加一個監(jiān)聽器到監(jiān)聽器數(shù)組的尾部。on(event, listener) 為指定事件注冊一個監(jiān)聽器,接受一個字符串 event 和一個回調(diào)函數(shù)。
server.on('connection', function (stream) {
console.log('someone connected!');
});
once(event, listener) 為指定事件注冊一個單次監(jiān)聽器,即 監(jiān)聽器最多只會觸發(fā)一次,觸發(fā)后立刻解除該監(jiān)聽器。
server.once('connection', function (stream) {
console.log('Ah, we have our first user!');
});
removeListener(event, listener) 移除指定事件的某個監(jiān)聽器,監(jiān)聽器必須是該事件已經(jīng)注冊過的監(jiān)聽器。
它接受兩個參數(shù),第一個是事件名稱,第二個是回調(diào)函數(shù)名稱。
var callback = function(stream) {
console.log('someone connected!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);
類方法 listenerCount(emitter, event) 返回指定事件的監(jiān)聽器數(shù)量。
events.emitter.listenerCount(eventName) //推薦
實例
以下實例通過 connection(連接)事件演示了 EventEmitter 類的應(yīng)用。
創(chuàng)建 main.js 文件,代碼如下:
var events = require('events');
var eventEmitter = new events.EventEmitter();
// 監(jiān)聽器 #1
var listener1 = function listener1() {
console.log('監(jiān)聽器 listener1 執(zhí)行。');
}
// 監(jiān)聽器 #2
var listener2 = function listener2() {
console.log('監(jiān)聽器 listener2 執(zhí)行。');
}
// 綁定 connection 事件,處理函數(shù)為 listener1
eventEmitter.addListener('connection', listener1);
// 綁定 connection 事件,處理函數(shù)為 listener2
eventEmitter.on('connection', listener2);
var eventListeners = eventEmitter.listenerCount('connection');
console.log(eventListeners + " 個監(jiān)聽器監(jiān)聽連接事件。");
// 處理 connection 事件
eventEmitter.emit('connection');
// 移除監(jiān)綁定的 listener1 函數(shù)
eventEmitter.removeListener('connection', listener1);
console.log("listener1 不再受監(jiān)聽。");
// 觸發(fā)連接事件
eventEmitter.emit('connection');
eventListeners = eventEmitter.listenerCount('connection');
console.log(eventListeners + " 個監(jiān)聽器監(jiān)聽連接事件。");
console.log("程序執(zhí)行完畢。");
error 事件
EventEmitter 定義了一個特殊的事件 error,它包含了錯誤的語義,我們在遇到 異常的時候通常會觸發(fā) error 事件。 我們一般要為會觸發(fā) error 事件的對象設(shè)置監(jiān)聽器,避免遇到錯誤后整個程序崩潰。例如:
var events = require('events');
var emitter = new events.EventEmitter();
emitter.emit('error');
繼承 EventEmitter
大多數(shù)時候我們不會直接使用 EventEmitter,而是在對象中繼承它。包括 fs、net、 http 在內(nèi)的,只要是支持事件響應(yīng)的核心模塊都是 EventEmitter 的子類。
Buffer(緩沖區(qū))
在 Node.js 中,Buffer 類是隨 Node 內(nèi)核一起發(fā)布的核心庫。Buffer 庫為 Node.js 帶來了一種存儲原始數(shù)據(jù)的方法,可以讓 Node.js 處理二進制數(shù)據(jù),每當(dāng)需要在 Node.js 中處理I/O操作中移動的數(shù)據(jù)時,就有可能使用 Buffer 庫。
Buffer 與字符編碼
Buffer 實例一般用于表示編碼字符的序列,比如 UTF-8 、 UCS2 、 Base64 、或十六進制編碼的數(shù)據(jù)。
const buf = Buffer.from('runoob', 'ascii');
// 輸出 72756e6f6f62
console.log(buf.toString('hex'));
// 輸出 cnVub29i
console.log(buf.toString('base64'));
創(chuàng)建 Buffer 類
Buffer 提供了以下 API 來創(chuàng)建 Buffer 類:
// 創(chuàng)建一個長度為 10、且用 0 填充的 Buffer。
const buf1 = Buffer.alloc(10);
// 創(chuàng)建一個長度為 10、且用 0x1 填充的 Buffer。
const buf2 = Buffer.alloc(10, 1);
// 創(chuàng)建一個長度為 10、且未初始化的 Buffer。
// 這個方法比調(diào)用 Buffer.alloc() 更快,
// 但返回的 Buffer 實例可能包含舊數(shù)據(jù),
// 因此需要使用 fill() 或 write() 重寫。
const buf3 = Buffer.allocUnsafe(10);
// 創(chuàng)建一個包含 [0x1, 0x2, 0x3] 的 Buffer。
const buf4 = Buffer.from([1, 2, 3]);
// 創(chuàng)建一個包含 UTF-8 字節(jié) [0x74, 0xc3, 0xa9, 0x73, 0x74] 的 Buffer。
const buf5 = Buffer.from('tést');
// 創(chuàng)建一個包含 Latin-1 字節(jié) [0x74, 0xe9, 0x73, 0x74] 的 Buffer。
const buf6 = Buffer.from('tést', 'latin1');
寫入緩沖區(qū)
語法:
buf.write(string[, offset[, length]][, encoding])
參數(shù):
string - 寫入緩沖區(qū)的字符串。 offset - 緩沖區(qū)開始寫入的索引值,默認為 0 。 length - 寫入的字節(jié)數(shù),默認為 buffer.length encoding - 使用的編碼。默認為 ‘utf8’ 。 根據(jù) encoding 的字符編碼寫入 string 到 buf 中的 offset 位置。 length 參數(shù)是寫入的字節(jié)數(shù)。 返回值 返回實際寫入的大小。 實例
buf = Buffer.alloc(256);
len = buf.write("www.runoob.com");
console.log("寫入字節(jié)數(shù) : "+ len);
從緩沖區(qū)讀取數(shù)據(jù)
語法
buf.toString([encoding[, start[, end]]])
參數(shù)
encoding - 使用的編碼。默認為 ‘utf8’ 。 start - 指定開始讀取的索引位置,默認為 0。 end - 結(jié)束位置,默認為緩沖區(qū)的末尾。
返回值 解碼緩沖區(qū)數(shù)據(jù)并使用指定的編碼返回字符串。
實例
buf = Buffer.alloc(26);
for (var i = 0 ; i < 26 ; i++) {
buf[i] = i + 97;
}
console.log( buf.toString('ascii')); // 輸出: abcdefghijklmnopqrstuvwxyz
console.log( buf.toString('ascii',0,5)); //使用 'ascii' 編碼, 并輸出: abcde
console.log( buf.toString('utf8',0,5)); // 使用 'utf8' 編碼, 并輸出: abcde
console.log( buf.toString(undefined,0,5)); // 使用默認的 'utf8' 編碼, 并輸出: abcde
將 Buffer 轉(zhuǎn)換為 JSON 對象
語法
buf.toJSON()
返回值 返回 JSON 對象。
實例
const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]);
const json = JSON.stringify(buf);
// 輸出: {"type":"Buffer","data":[1,2,3,4,5]}
console.log(json);
const copy = JSON.parse(json, (key, value) => {
return value && value.type === 'Buffer' ?
Buffer.from(value.data) :
value;
});
// 輸出:
console.log(copy);
JSON 通常用于與服務(wù)端交換數(shù)據(jù)。 我們可以使用 JSON.parse() 方法將數(shù)據(jù)轉(zhuǎn)換為 JavaScript 對象。
緩沖區(qū)合并
語法
Buffer.concat(list[, totalLength])
參數(shù)
list - 用于合并的 Buffer 對象數(shù)組列表。 totalLength - 指定合并后Buffer對象的總長度。
返回值 返回一個多個成員合并的新 Buffer 對象。
實例
var buffer1 = Buffer.from(('菜鳥教程'));
var buffer2 = Buffer.from(('www.runoob.com'));
var buffer3 = Buffer.concat([buffer1,buffer2]);
console.log("buffer3 內(nèi)容: " + buffer3.toString());
緩沖區(qū)比較
語法
buf.compare(otherBuffer);
參數(shù)
otherBuffer - 與 buf 對象比較的另外一個 Buffer 對象。
返回值 返回一個數(shù)字,表示 buf 在 otherBuffer 之前,之后或相同。
實例
var buffer1 = Buffer.from('ABC');
var buffer2 = Buffer.from('ABCD');
var result = buffer1.compare(buffer2);
if(result < 0) {
console.log(buffer1 + " 在 " + buffer2 + "之前");
}else if(result == 0){
console.log(buffer1 + " 與 " + buffer2 + "相同");
}else {
console.log(buffer1 + " 在 " + buffer2 + "之后");
}
拷貝緩沖區(qū)
語法
buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]])
參數(shù)
targetBuffer - 要拷貝的 Buffer 對象。 targetStart - 數(shù)字, 可選, 默認: 0 sourceStart - 數(shù)字, 可選, 默認: 0 sourceEnd - 數(shù)字, 可選, 默認: buffer.length
實例
var buf1 = Buffer.from('abcdefghijkl');
var buf2 = Buffer.from('RUNOOB');
//將 buf2 插入到 buf1 指定位置上
buf2.copy(buf1, 2);
console.log(buf1.toString());
緩沖區(qū)裁剪
語法
buf.slice([start[, end]])
參數(shù)
start - 數(shù)字, 可選, 默認: 0 end - 數(shù)字, 可選, 默認: buffer.length
返回值 返回一個新的緩沖區(qū),它和舊緩沖區(qū)指向同一塊內(nèi)存,但是從索引 start 到 end 的位置剪切。
實例
var buffer1 = Buffer.from('runoob');
// 剪切緩沖區(qū)
var buffer2 = buffer1.slice(0,2);
console.log("buffer2 content: " + buffer2.toString());
緩沖區(qū)長度
語法
buf.length;
返回值 返回 Buffer 對象所占據(jù)的內(nèi)存長度。
實例
var buffer = Buffer.from('www.runoob.com');
// 緩沖區(qū)長度
console.log("buffer length: " + buffer.length);
Stream(流)
Stream 是一個抽象接口,Node 中有很多對象實現(xiàn)了這個接口。
所有的 Stream 對象都是 EventEmitter 的實例。常用的事件有:
data - 當(dāng)有數(shù)據(jù)可讀時觸發(fā)。 end - 沒有更多的數(shù)據(jù)可讀時觸發(fā)。 error - 在接收和寫入過程中發(fā)生錯誤時觸發(fā)。 finish - 所有數(shù)據(jù)已被寫入到底層系統(tǒng)時觸發(fā)。
從流中讀取數(shù)據(jù)
創(chuàng)建 input.txt 文件,內(nèi)容如下:
菜鳥教程官網(wǎng)地址:www.runoob.com
創(chuàng)建 main.js 文件, 代碼如下:
var fs = require("fs");
var data = '';
// 創(chuàng)建可讀流
var readerStream = fs.createReadStream('input.txt');
// 設(shè)置編碼為 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("程序執(zhí)行完畢");
寫入流
var fs = require("fs");
var data = '菜鳥教程官網(wǎng)地址:www.runoob.com';
// 創(chuàng)建一個可以寫入的流,寫入到文件 output.txt 中
var writerStream = fs.createWriteStream('output.txt');
// 使用 utf8 編碼寫入數(shù)據(jù)
writerStream.write(data,'UTF8');
// 標(biāo)記文件末尾
writerStream.end();
// 處理流事件 --> finish、error
writerStream.on('finish', function() {
console.log("寫入完成。");
});
writerStream.on('error', function(err){
console.log(err.stack);
});
console.log("程序執(zhí)行完畢");
管道流
管道提供了一個輸出流到輸入流的機制。通常我們用于從一個流中獲取數(shù)據(jù)并將數(shù)據(jù)傳遞到另外一個流中。
以下實例我們通過讀取一個文件內(nèi)容并將內(nèi)容寫入到另外一個文件中。
設(shè)置 input.txt 文件內(nèi)容如下:
菜鳥教程官網(wǎng)地址:www.runoob.com
管道流操作實例
創(chuàng)建 main.js 文件, 代碼如下:
var fs = require("fs");
// 創(chuàng)建一個可讀流
var readerStream = fs.createReadStream('input.txt');
// 創(chuàng)建一個可寫流
var writerStream = fs.createWriteStream('output.txt');
// 管道讀寫操作
// 讀取 input.txt 文件內(nèi)容,并將內(nèi)容寫入到 output.txt 文件中
readerStream.pipe(writerStream);
console.log("程序執(zhí)行完畢");
鏈?zhǔn)搅?/p>
鏈?zhǔn)绞峭ㄟ^連接輸出流到另外一個流并創(chuàng)建多個流操作鏈的機制。鏈?zhǔn)搅饕话阌糜诠艿啦僮鳌?/p>
接下來我們就是用管道和鏈?zhǔn)絹韷嚎s和解壓文件。
創(chuàng)建 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("文件壓縮完成。");
執(zhí)行完以上操作后,我們可以看到當(dāng)前目錄下生成了 input.txt 的壓縮文件 input.txt.gz。
接下來,讓我們來解壓該文件,創(chuàng)建 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('input.txt'));
console.log("文件解壓完成。");
模塊系統(tǒng)
一個 Node.js 文件就是一個模塊,這個文件可能是JavaScript 代碼、JSON 或者編譯過的C/C++ 擴展。
引入模塊
我們創(chuàng)建一個 main.js 文件并引入 hello 模塊,代碼如下:
var hello = require('./hello');
hello.world();
以上實例中,代碼 require(‘./hello’) 引入了當(dāng)前目錄下的 hello.js 文件(./ 為當(dāng)前目錄,node.js 默認后綴為 js)。
接下來我們就來創(chuàng)建 hello.js 文件,代碼如下:
exports.world = function() {
console.log('Hello World');
}
有時候我們只是想把一個對象封裝到模塊中,格式如下:
//hello.js
function Hello() {
var name;
this.setName = function(thyName) {
name = thyName;
};
this.sayHello = function() {
console.log('Hello ' + name);
};
};
module.exports = Hello;
這樣就可以直接獲得這個對象了:
//main.js
var Hello = require('./hello');
hello = new Hello();
hello.setName('BYVoid');
hello.sayHello();
函數(shù)
在 JavaScript中,一個函數(shù)可以作為另一個函數(shù)的參數(shù)。我們可以先定義一個函數(shù),然后傳遞,也可以在傳遞參數(shù)的地方直接定義函數(shù)。
function say(word) {
console.log(word);
}
function execute(someFunction, value) {
someFunction(value);
}
execute(say, "Hello");
以上代碼中,我們把 say 函數(shù)作為 execute 函數(shù)的第一個變量進行了傳遞。這里傳遞的不是 say 的返回值,而是 say 本身!
這樣一來, say 就變成了execute 中的本地變量 someFunction ,execute 可以通過調(diào)用 someFunction() (帶括號的形式)來使用 say 函數(shù)。
當(dāng)然,因為 say 有一個變量, execute 在調(diào)用 someFunction 時可以傳遞這樣一個變量。
匿名函數(shù)
我們可以把一個函數(shù)作為變量傳遞。但是我們不一定要"先定義,再傳遞"。
我們可以直接在另一個函數(shù)的括號中定義和傳遞這個函數(shù):
function execute(someFunction, value) {
someFunction(value);
}
execute(function(word){ console.log(word) }, "Hello");
我們在 execute 接受第一個參數(shù)的地方直接定義了我們準(zhǔn)備傳遞給 execute 的函數(shù)。
用這種方式,我們甚至不用給這個函數(shù)起名字,這也是為什么它被叫做匿名函數(shù) 。
函數(shù)傳遞是如何讓 HTTP 服務(wù)器工作的
var http = require("http");
http.createServer(function(request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}).listen(8888);
現(xiàn)在它看上去應(yīng)該清晰了很多:我們向 createServer 函數(shù)傳遞了一個匿名函數(shù)。
路由
我們需要的所有數(shù)據(jù)都會包含在 request 對象中,該對象作為 onRequest() 回調(diào)函數(shù)的第一個參數(shù)傳遞。
但是為了解析這些數(shù)據(jù),我們需要額外的 Node.JS 模塊,它們分別是 url 和 querystring 模塊。
url.parse(string).query
|
url.parse(string).pathname |
| |
| |
------|-------------------
http://localhost:8888/start?foo=bar&hello=world
--- -----
| |
| |
querystring.parse(queryString)["foo"] |
|
querystring.parse(queryString)["hello"]
現(xiàn)在我們來給 onRequest() 函數(shù)加上一些邏輯,用來找出瀏覽器請求的 URL 路徑:
var http = require("http");
var url = require("url");
function start() {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received.");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}
exports.start = start;
現(xiàn)在我們可以來編寫路由了,建立一個名為 router.js 的文件,添加以下內(nèi)容:
function route(pathname) {
console.log("About to route a request for " + pathname);
}
exports.route = route;
如你所見,這段代碼什么也沒干,不過對于現(xiàn)在來說這是應(yīng)該的。在添加更多的邏輯以前,我們先來看看如何把路由和服務(wù)器整合起來。
首先,我們來擴展一下服務(wù)器的 start() 函數(shù),以便將路由函數(shù)作為參數(shù)傳遞過去,server.js 文件代碼如下
var http = require("http");
var url = require("url");
function start(route) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received.");
route(pathname);
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}
exports.start = start;
同時,我們會相應(yīng)擴展 index.js,使得路由函數(shù)可以被注入到服務(wù)器中:
var server = require("./server");
var router = require("./router");
server.start(router.route);
如果現(xiàn)在啟動應(yīng)用(node index.js,始終記得這個命令行),隨后請求一個URL,你將會看到應(yīng)用輸出相應(yīng)的信息,這表明我們的HTTP服務(wù)器已經(jīng)在使用路由模塊了,并會將請求的路徑傳遞給路由:
瀏覽器訪問 http://127.0.0.1:8888/,輸出結(jié)果如下:
全局對象
全局對象(Global Object),它及其所有屬性都可以在程序的任何地方訪問。
Node.js 中的全局對象是 global,所有全局變量都是 global 對象的屬性。
在 Node.js 我們可以直接訪問到 global 的屬性,而不需要在應(yīng)用中包含它。
__filename
__filename 表示當(dāng)前正在執(zhí)行的腳本的文件名。它將輸出文件所在位置的絕對路徑。
實例 創(chuàng)建文件 main.js ,代碼如下所示:
// 輸出全局變量 __filename 的值
console.log( __filename );
__dirname
__dirname 表示當(dāng)前執(zhí)行腳本所在的目錄。
實例 創(chuàng)建文件 main.js ,代碼如下所示:
// 輸出全局變量 __dirname 的值
console.log( __dirname );
setTimeout(cb, ms)
setTimeout(cb, ms) 全局函數(shù)在指定的毫秒(ms)數(shù)后執(zhí)行指定函數(shù)(cb)。 實例
function printHello(){
console.log( "Hello, World!");
}
// 兩秒后執(zhí)行以上函數(shù)
setTimeout(printHello, 2000);
兩秒后輸出:
clearTimeout(t)
clearTimeout( t ) 全局函數(shù)用于停止一個之前通過 setTimeout() 創(chuàng)建的定時器。
參數(shù) t 是通過 setTimeout() 函數(shù)創(chuàng)建的定時器。
實例
function printHello(){
console.log( "Hello, World!");
}
// 兩秒后執(zhí)行以上函數(shù)
var t = setTimeout(printHello, 2000);
// 清除定時器
clearTimeout(t);
setInterval(cb, ms)
setInterval(cb, ms) 全局函數(shù)在指定的毫秒(ms)數(shù)后執(zhí)行指定函數(shù)(cb)。
可以使用 clearInterval(t) 函數(shù)來清除定時器。
setInterval() 方法會不停地調(diào)用函數(shù),直到 clearInterval() 被調(diào)用或窗口被關(guān)閉。
實例
function printHello(){
console.log( "Hello, World!");
}
// 兩秒后執(zhí)行以上函數(shù)
setInterval(printHello, 2000);
以上程序每隔兩秒就會輸出一次"Hello, World!",且會永久執(zhí)行下去,直到你按下 ctrl + c 按鈕。
console
console 用于提供控制臺標(biāo)準(zhǔn)輸出。
console.log([data][, …]) 向標(biāo)準(zhǔn)輸出流打印字符并以換行符結(jié)束。console.info([data][, …]) 這個命令與console.log差別并不大console.time(label) 輸出時間,表示計時開始。console.timeEnd(label) 結(jié)束時間,表示計時結(jié)束。
實例
console.info("程序開始執(zhí)行:");
var counter = 10;
console.log("計數(shù): %d", counter);
console.time("獲取數(shù)據(jù)");
//
// 執(zhí)行一些代碼
//
console.timeEnd('獲取數(shù)據(jù)');
console.info("程序執(zhí)行完畢。")
process
process 是一個全局變量,即 global 對象的屬性。
它用于描述當(dāng)前Node.js 進程狀態(tài)的對象,提供了一個與操作系統(tǒng)的簡單接口。
實例 Process 對象的常用的成員方法:exit 當(dāng)進程準(zhǔn)備退出時觸發(fā)。
process.on('exit', function(code) {
// 以下代碼永遠不會執(zhí)行
setTimeout(function() {
console.log("該代碼不會執(zhí)行");
}, 0);
console.log('退出碼為:', code);
});
console.log("程序執(zhí)行結(jié)束");
Process 屬性
stdout 標(biāo)準(zhǔn)輸出流。argv argv 屬性返回一個數(shù)組,由命令行執(zhí)行腳本時的各個參數(shù)組成。 它的第一個成員總是node,第二個成員是腳本文件名,其余成員是腳本文件的參數(shù)。execPath 返回執(zhí)行當(dāng)前腳本的 Node 二進制文件的絕對路徑。platform 運行程序所在的平臺系統(tǒng) ‘darwin’, ‘freebsd’, ‘linux’, ‘sunos’ 或 ‘win32’
// 輸出到終端
process.stdout.write("Hello World!" + "\n");
// 通過參數(shù)讀取
process.argv.forEach(function(val, index, array) {
console.log(index + ': ' + val);
});
// 獲取執(zhí)行路徑
console.log(process.execPath);
// 平臺信息
console.log(process.platform);
方法參考手冊
// 輸出當(dāng)前目錄
console.log('當(dāng)前目錄: ' + process.cwd());
// 輸出當(dāng)前版本
console.log('當(dāng)前版本: ' + process.version);
// 輸出內(nèi)存使用情況
console.log(process.memoryUsage());
常用工具
util 是一個Node.js 核心模塊,提供常用函數(shù)的集合。
使用方法如下:
const util = require('util');
util.callbackify
util.callbackify(original) 將 async 異步函數(shù)(或者一個返回值為 Promise 的函數(shù))轉(zhuǎn)換成遵循異常優(yōu)先的回調(diào)風(fēng)格的函數(shù)。
const util = require('util');
async function fn() {
return 'hello world';
}
const callbackFunction = util.callbackify(fn);
callbackFunction((err, ret) => {
if (err) throw err;
console.log(ret);
});
util.inherits
util.inherits(constructor, superConstructor) 是一個實現(xiàn)對象間原型繼承的函數(shù)。
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);
我們定義了一個基礎(chǔ)對象 Base 和一個繼承自 Base 的 Sub。 Base 有三個在構(gòu)造函數(shù)內(nèi)定義的屬性和一個原型中定義的函數(shù),通過util.inherits 實現(xiàn)繼承。運行結(jié)果如下:
注意:Sub 僅僅繼承了Base 在原型中定義的函數(shù),而構(gòu)造函數(shù)內(nèi)部創(chuàng)造的 base 屬性和 sayHello 函數(shù)都沒有被 Sub 繼承。
如果我們?nèi)サ?objSub.sayHello(); 這行的注釋,將會看到:
util.inspect
util.inspect(object,[showHidden],[depth],[colors]) 是一個將任意對象轉(zhuǎn)換 為字符串的方法。
showHidden 是一個可選參數(shù),如果值為 true,將會輸出更多隱藏信息。
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));
util.isArray
如果給定的參數(shù) “object” 是一個數(shù)組返回 true,否則返回 false。
var util = require('util');
util.isArray([])
// true
util.isArray(new Array)
// true
util.isArray({})
// false
util.isRegExp(object)
如果給定的參數(shù) “object” 是一個正則表達式返回true,否則返回false。
var util = require('util');
util.isRegExp(/some regexp/)
// true
util.isRegExp(new RegExp('another regexp'))
// true
util.isRegExp({})
// false
util.isDate(object)
如果給定的參數(shù) “object” 是一個日期返回true,否則返回false。
var util = require('util');
util.isDate(new Date())
// true
util.isDate(Date())
// false (without 'new' returns a String)
util.isDate({})
// false
文件系統(tǒng)
Node 導(dǎo)入文件系統(tǒng)模塊(fs)語法如下所示:
var fs = require("fs")
異步和同步
異步的方法函數(shù)最后一個參數(shù)為回調(diào)函數(shù),回調(diào)函數(shù)的第一個參數(shù)包含了錯誤信息(error)。
建議大家使用異步方法,比起同步,異步方法性能更高,速度更快,而且沒有阻塞。
實例 創(chuàng)建 input.txt 文件,內(nèi)容如下:
菜鳥教程官網(wǎng)地址:www.runoob.com
文件讀取實例
創(chuàng)建 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("程序執(zhí)行完畢。");
打開文件
語法
fs.open(path, flags[, mode], callback)
參數(shù)
path - 文件的路徑。 flags - 文件打開的行為。flags 參數(shù)可以是以下值:
r 以讀取模式打開文件。w 以寫入模式打開文件。r+/w+ 以讀寫模式打開文件。 mode - 設(shè)置文件模式(權(quán)限),文件創(chuàng)建默認權(quán)限為 0666(可讀,可寫)。 callback - 回調(diào)函數(shù),帶有兩個參數(shù)如:callback(err, fd)。
實例 創(chuàng)建 file.js 文件,并打開 input.txt 文件進行讀寫,代碼如下所示:
var fs = require("fs");
// 異步打開文件
console.log("準(zhǔn)備打開文件!");
fs.open('input.txt', 'r+', function(err, fd) {
if (err) {
return console.error(err);
}
console.log("文件打開成功!");
});
獲取文件信息
語法 以下為通過異步模式獲取文件信息的語法格式:
fs.stat(path, callback)
參數(shù)
path - 文件路徑。callback - 回調(diào)函數(shù),帶有兩個參數(shù)如:(err, stats), stats 是 fs.Stats 對象。
fs.stat(path)執(zhí)行后,會將 stats 類的實例返回給其回調(diào)函數(shù)??梢酝ㄟ^ stats 類中的提供方法判斷文件的相關(guān)屬性。例如判斷是否為文件:
var fs = require('fs');
fs.stat('/Users/liuht/code/itbilu/demo/fs.js', function (err, stats) {
console.log(stats.isFile()); //true
})
實例
var fs = require("fs");
console.log("準(zhǔn)備打開文件!");
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());
});
寫入文件
語法
fs.writeFile(file, data[, options], callback)
writeFile 直接打開文件默認是 w 模式,所以如果文件存在,該方法寫入的內(nèi)容會覆蓋舊的文件內(nèi)容。 參數(shù)
file - 文件名或文件描述符。 data - 要寫入文件的數(shù)據(jù),可以是 String(字符串) 或 Buffer(緩沖) 對象。 options - 該參數(shù)是一個對象,包含 {encoding, mode, flag}。默認編碼為 utf8, 模式為 0666 , flag 為 ‘w’ callback - 回調(diào)函數(shù),回調(diào)函數(shù)只包含錯誤信息參數(shù)(err),在寫入失敗時返回。
實例 接下來我們創(chuàng)建 file.js 文件,代碼如下所示:
var fs = require("fs");
console.log("準(zhǔn)備寫入文件");
fs.writeFile('input.txt', '我是通 過fs.writeFile 寫入文件的內(nèi)容', function(err) {
if (err) {
return console.error(err);
}
console.log("數(shù)據(jù)寫入成功!");
console.log("--------我是分割線-------------")
console.log("讀取寫入的數(shù)據(jù)!");
fs.readFile('input.txt', function (err, data) {
if (err) {
return console.error(err);
}
console.log("異步讀取文件數(shù)據(jù): " + data.toString());
});
});
讀取文件
語法 以下為異步模式下讀取文件的語法格式:
fs.read(fd, buffer, offset, length, position, callback)
參數(shù)
fd - 通過 fs.open() 方法返回的文件描述符。 buffer - 數(shù)據(jù)寫入的緩沖區(qū)。 offset - 緩沖區(qū)寫入的寫入偏移量。 length - 要從文件中讀取的字節(jié)數(shù)。 position - 文件讀取的起始位置,如果 position 的值為 null,則會從當(dāng)前文件指針的位置讀取。 callback - 回調(diào)函數(shù),有三個參數(shù)err, bytesRead, buffer,err 為錯誤信息, bytesRead 表示讀取的字節(jié)數(shù),buffer 為緩沖區(qū)對象。
實例 input.txt 文件內(nèi)容為:
菜鳥教程官網(wǎng)地址:www.runoob.com
file.js
var fs = require("fs");
var buf = new Buffer.alloc(1024);
console.log("準(zhǔn)備打開已存在的文件!");
fs.open('input.txt', 'r+', function(err, fd) {
if (err) {
return console.error(err);
}
console.log("文件打開成功!");
console.log("準(zhǔn)備讀取文件:");
fs.read(fd, buf, 0, buf.length, 0, function(err, bytes){
if (err){
console.log(err);
}
console.log(bytes + " 字節(jié)被讀取");
// 僅輸出讀取的字節(jié)
if(bytes > 0){
console.log(buf.slice(0, bytes).toString());
}
});
});
關(guān)閉文件
語法
fs.close(fd, callback)
參數(shù)
fd - 通過 fs.open() 方法返回的文件描述符。 callback - 回調(diào)函數(shù),沒有參數(shù)。
實例 file.js
var fs = require("fs");
var buf = new Buffer.alloc(1024);
console.log("準(zhǔn)備打開文件!");
fs.open('input.txt', 'r+', function(err, fd) {
if (err) {
return console.error(err);
}
console.log("文件打開成功!");
console.log("準(zhǔn)備讀取文件!");
fs.read(fd, buf, 0, buf.length, 0, function(err, bytes){
if (err){
console.log(err);
}
// 僅輸出讀取的字節(jié)
if(bytes > 0){
console.log(buf.slice(0, bytes).toString());
}
// 關(guān)閉文件
fs.close(fd, function(err){
if (err){
console.log(err);
}
console.log("文件關(guān)閉成功");
});
});
});
截取文件
語法
fs.ftruncate(fd, len, callback)
參數(shù)
fd - 通過 fs.open() 方法返回的文件描述符。 len - 文件內(nèi)容截取的長度。 callback - 回調(diào)函數(shù),沒有參數(shù)。
實例 input.txt 文件內(nèi)容為:
site:www.runoob.com
file.js
var fs = require("fs");
var buf = new Buffer.alloc(1024);
console.log("準(zhǔn)備打開文件!");
fs.open('input.txt', 'r+', function(err, fd) {
if (err) {
return console.error(err);
}
console.log("文件打開成功!");
console.log("截取10字節(jié)內(nèi)的文件內(nèi)容,超出部分將被去除。");
// 截取文件
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);
}
// 僅輸出讀取的字節(jié)
if(bytes > 0){
console.log(buf.slice(0, bytes).toString());
}
// 關(guān)閉文件
fs.close(fd, function(err){
if (err){
console.log(err);
}
console.log("文件關(guān)閉成功!");
});
});
});
});
刪除文件
語法
fs.unlink(path, callback)
參數(shù)
path - 文件路徑。 callback - 回調(diào)函數(shù),沒有參數(shù)。
實例 file.js
var fs = require("fs");
console.log("準(zhǔn)備刪除文件!");
fs.unlink('input.txt', function(err) {
if (err) {
return console.error(err);
}
console.log("文件刪除成功!");
});
創(chuàng)建目錄
語法
fs.mkdir(path[, options], callback)
參數(shù)
path - 文件路徑。 options 參數(shù)可以是:
recursive - 是否以遞歸的方式創(chuàng)建目錄,默認為 false。mode - 設(shè)置目錄權(quán)限,默認為 0777。 callback - 回調(diào)函數(shù),沒有參數(shù)。
實例 file.js
var fs = require("fs");
// tmp 目錄必須存在
console.log("創(chuàng)建目錄 /tmp/test/");
fs.mkdir("./tmp/test/",function(err){
if (err) {
return console.error(err);
}
console.log("目錄創(chuàng)建成功。");
});
讀取目錄
語法
fs.readdir(path, callback)
參數(shù)
path - 文件路徑。callback - 回調(diào)函數(shù),回調(diào)函數(shù)帶有兩個參數(shù)err, files,err 為錯誤信息,files 為 目錄下的文件數(shù)組列表。
實例 file.js
var fs = require("fs");
console.log("查看 /tmp 目錄");
fs.readdir("/tmp/",function(err, files){
if (err) {
return console.error(err);
}
files.forEach( function (file){
console.log( file );
});
});
刪除目錄
語法
fs.rmdir(path, callback)
參數(shù)
path - 文件路徑。callback - 回調(diào)函數(shù),沒有參數(shù)。
實例 file.js
var fs = require("fs");
// 執(zhí)行前創(chuàng)建一個空的 /tmp/test 目錄
console.log("準(zhǔn)備刪除目錄 /tmp/test");
fs.rmdir("/tmp/test",function(err){
if (err) {
return console.error(err);
}
console.log("讀取 /tmp 目錄");
fs.readdir("/tmp/",function(err, files){
if (err) {
return console.error(err);
}
files.forEach( function (file){
console.log( file );
});
});
});
GET/POST 請求
在很多場景中,我們的服務(wù)器都需要跟用戶的瀏覽器打交道,如表單提交。
表單提交到服務(wù)器一般都使用 GET/POST 請求。
獲取 GET 請求內(nèi)容
由于GET請求直接被嵌入在路徑中,URL是完整的請求路徑,包括了?后面的部分,因此你可以手動解析后面的內(nèi)容作為GET請求的參數(shù)。
node.js 中 url 模塊中的 parse 函數(shù)提供了這個功能。
var http = require('http');
var url = require('url');
var util = require('util');
http.createServer(function(req, res){
res.writeHead(200, {'Content-Type': 'text/plain'});
// 解析 url 參數(shù)
var params = url.parse(req.url, true).query;
res.write("網(wǎng)站名:" + params.name);
res.write("\n");
res.write("網(wǎng)站 URL:" + params.url);
res.end();
}).listen(3000);
獲取 POST 請求內(nèi)容
POST 請求的內(nèi)容全部的都在請求體中。
node.js 默認是不會解析請求體的,當(dāng)你需要的時候,需要手動來做。
var http = require('http');
var querystring = require('querystring');
var util = require('util');
http.createServer(function(req, res){
// 定義了一個post變量,用于暫存請求體的信息
var post = '';
// 通過req的data事件監(jiān)聽函數(shù),每當(dāng)接受到請求體的數(shù)據(jù),就累加到post變量中
req.on('data', function(chunk){
post += chunk;
});
// 在end事件觸發(fā)后,通過querystring.parse將post解析為真正的POST請求格式,然后向客戶端返回。
req.on('end', function(){
post = querystring.parse(post);
res.end(util.inspect(post));
});
}).listen(3000);
Web 模塊
目前最主流的三個Web服務(wù)器是Apache、Nginx、IIS。
Web 應(yīng)用架構(gòu)
Client - 客戶端,一般指瀏覽器,瀏覽器可以通過 HTTP 協(xié)議向服務(wù)器請求數(shù)據(jù)。 Server - 服務(wù)端,一般指 Web 服務(wù)器,可以接收客戶端請求,并向客戶端發(fā)送響應(yīng)數(shù)據(jù)。 Business - 業(yè)務(wù)層, 通過 Web 服務(wù)器處理應(yīng)用程序,如與數(shù)據(jù)庫交互,邏輯運算,調(diào)用外部程序等。 Data - 數(shù)據(jù)層,一般由數(shù)據(jù)庫組成。
使用 Node 創(chuàng)建 Web 服務(wù)器
Node.js 提供了 http 模塊,http 模塊主要用于搭建 HTTP 服務(wù)端和客戶端。
以下是演示一個最基本的 HTTP 服務(wù)器架構(gòu)(使用 8080 端口),創(chuàng)建server.js文件,代碼如下所示:
var http = require('http');
var fs = require('fs');
var url = require('url');
// 創(chuàng)建服務(wù)器
http.createServer( function (request, response) {
// 解析請求,包括文件名
var pathname = url.parse(request.url).pathname;
// 輸出請求的文件名
console.log("Request for " + pathname + " received.");
// 從文件系統(tǒng)中讀取請求的文件內(nèi)容
fs.readFile(pathname.substr(1), function (err, data) {
if (err) {
console.log(err);
// HTTP 狀態(tài)碼: 404 : NOT FOUND
// Content Type: text/html
response.writeHead(404, {'Content-Type': 'text/html'});
}else{
// HTTP 狀態(tài)碼: 200 : OK
// Content Type: text/html
response.writeHead(200, {'Content-Type': 'text/html'});
// 響應(yīng)文件內(nèi)容
response.write(data.toString());
}
// 發(fā)送響應(yīng)數(shù)據(jù)
response.end();
});
}).listen(8080);
// 控制臺會輸出以下信息
console.log('Server running at http://127.0.0.1:8080/');
執(zhí)行 server.js 程序: 打開地址:
使用 Node 創(chuàng)建 Web 客戶端
client.js
var http = require('http');
// 用于請求的選項
var options = {
host: 'localhost',
port: '8080',
path: '/index.html'
};
// 處理響應(yīng)的回調(diào)函數(shù)
var callback = function(response){
// 不斷更新數(shù)據(jù)
var body = '';
response.on('data', function(data) {
body += data;
});
response.on('end', function() {
// 數(shù)據(jù)接收完成
console.log(body);
});
}
// 向服務(wù)端發(fā)送請求
var req = http.request(options, callback);
req.end();
在 server.js 啟動的情況下,ctrl + shift + ` 新開一個終端,執(zhí)行 client.js 文件: 客戶端請求信息,執(zhí)行 server.js 的控制臺輸出:
Express 框架
使用 Express 可以快速地搭建一個完整功能的網(wǎng)站。
安裝
cnpm install express
cnpm install body-parser
cnpm install cookie-parser
cnpm install multer
以下幾個重要的模塊是需要與 express 框架一起安裝的:
body-parser - 用于處理 JSON, Raw, Text 和 URL 編碼的數(shù)據(jù)。 cookie-parser - 這就是一個解析Cookie的工具。通過req.cookies可以取到傳過來的cookie,并把它們轉(zhuǎn)成對象。 multer - 用于處理 enctype=“multipart/form-data”(設(shè)置表單的MIME編碼)的表單數(shù)據(jù)。
第一個 Express 框架實例
以下實例中我們引入了 express 模塊,并在客戶端發(fā)起請求后,響應(yīng) “Hello World” 字符串。
創(chuàng)建 express_demo.js 文件,代碼如下所示:
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.send('Hello World');
})
var server = app.listen(8081, '127.0.0.1',function () {
var host = server.address().address
var port = server.address().port
console.log("應(yīng)用實例,訪問地址為 http://%s:%s", host, port)
})
請求和響應(yīng)
Express 應(yīng)用使用回調(diào)函數(shù)的參數(shù): request 和 response 對象來處理請求和響應(yīng)的數(shù)據(jù)。
app.get('/', function (req, res) {
// --
})
Request 對象 - request 對象表示 HTTP 請求,包含了請求查詢字符串,參數(shù),內(nèi)容,HTTP 頭部等屬性。Response 對象 - response 對象表示 HTTP 響應(yīng),即在接收到請求時向客戶端發(fā)送的 HTTP 響應(yīng)數(shù)據(jù)。
路由
路由決定了由誰(指定腳本)去響應(yīng)客戶端請求。
在HTTP請求中,我們可以通過路由提取出請求的URL以及GET/POST參數(shù)。
express_demo2.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 頁面響應(yīng)
app.get('/del_user', function (req, res) {
console.log("/del_user 響應(yīng) DELETE 請求");
res.send('刪除頁面');
})
// /list_user 頁面 GET 請求
app.get('/list_user', function (req, res) {
console.log("/list_user GET 請求");
res.send('用戶列表頁面');
})
// 對頁面 abcd, abxcd, ab123cd, 等響應(yīng) GET 請求
app.get('/ab*cd', function(req, res) {
console.log("/ab*cd GET 請求");
res.send('正則匹配');
})
var server = app.listen(8081,'127.0.0.1',function () {
var host = server.address().address
var port = server.address().port
console.log("應(yīng)用實例,訪問地址為 http://%s:%s", host, port)
})
靜態(tài)文件
你可以使用express.static中間件來設(shè)置靜態(tài)文件路徑。例如,如果你將圖片, CSS, JavaScript 文件放在 public 目錄下,你可以這么寫:
app.use('/public', express.static('public'));
創(chuàng)建文件夾public/images,放入圖片logo.png express_demo3.js
var express = require('express');
var app = express();
app.use('/public', express.static('public'));
app.get('/', function (req, res) {
res.send('Hello World');
})
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("應(yīng)用實例,訪問地址為 http://%s:%s", host, port)
})
訪問網(wǎng)頁:http://127.0.0.1:8081/public/images/logo.png
得到logo.png:
GET 方法
在表單中通過 GET 方法提交兩個參數(shù),我們可以使用 server.js 文件內(nèi)的 process_get 路由器來處理輸入:
index.html 文件代碼:
server.js 文件代碼:
var express = require('express');
var app = express();
app.use('/public', express.static('public'));
app.get('/index.html', function (req, res) {
res.sendFile( __dirname + "/" + "index.html" );
})
app.get('/process_get', function (req, res) {
// 輸出 JSON 格式
var response = {
"first_name":req.query.first_name,
"last_name":req.query.last_name
};
console.log(response);
res.end(JSON.stringify(response));
})
var server = app.listen(8081,'127.0.0.1',function () {
var host = server.address().address
var port = server.address().port
console.log("應(yīng)用實例,訪問地址為 http://%s:%s", host, port)
})
POST 方法
在表單中通過 POST 方法提交兩個參數(shù),我們可以使用 server.js 文件內(nèi)的 process_post 路由器來處理輸入:
index.html 文件代碼:
server.js 文件代碼:
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
// 創(chuàng)建 application/x-www-form-urlencoded 編碼解析
var urlencodedParser = bodyParser.urlencoded({ extended: false })
app.use('/public', express.static('public'));
app.get('/index.html', function (req, res) {
res.sendFile( __dirname + "/" + "index.html" );
})
app.post('/process_post', urlencodedParser, function (req, res) {
// 輸出 JSON 格式
var response = {
"first_name":req.body.first_name,
"last_name":req.body.last_name
};
console.log(response);
res.end(JSON.stringify(response));
})
var server = app.listen(8081,'127.0.0.1',function () {
var host = server.address().address
var port = server.address().port
console.log("應(yīng)用實例,訪問地址為 http://%s:%s", host, port)
})
文件上傳
創(chuàng)建一個用于上傳文件的表單,使用 POST 方法,表單 enctype 屬性設(shè)置為 multipart/form-data。
index.html 文件代碼:
文件上傳:
選擇一個文件上傳:
server.js 文件代碼:
var express = require('express');
var app = express();
var fs = require("fs");
var bodyParser = require('body-parser');
var multer = require('multer');
app.use('/public', express.static('public'));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(multer({ dest: '/tmp/'}).array('image'));
app.get('/index.html', function (req, res) {
res.sendFile( __dirname + "/" + "index.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,'127.0.0.1',function () {
var host = server.address().address
var port = server.address().port
console.log("應(yīng)用實例,訪問地址為 http://%s:%s", host, port)
})
Cookie 管理
使用中間件向 Node.js 服務(wù)器發(fā)送 cookie 信息,以下代碼輸出了客戶端發(fā)送的 cookie 信息: express_cookie.js 文件代碼:
// express_cookie.js 文件
var express = require('express')
var cookieParser = require('cookie-parser')
var util = require('util');
var app = express()
app.use(cookieParser())
app.get('/', function(req, res) {
console.log("Cookies: " + util.inspect(req.cookies));
})
app.listen(8081)
訪問 http://127.0.0.1:8081,并查看終端信息:
RESTful API
REST即表述性狀態(tài)傳遞(英文:Representational State Transfer)。
REST 通常使用 JSON 數(shù)據(jù)格式。
以下為 REST 基本架構(gòu)的四個方法:
GET - 用于獲取數(shù)據(jù)。 PUT - 用于更新或添加數(shù)據(jù)。 DELETE - 用于刪除數(shù)據(jù)。 POST - 用于添加數(shù)據(jù)。
創(chuàng)建 RESTful
首先,創(chuàng)建一個 json 數(shù)據(jù)資源文件 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
}
}
獲取用戶列表:
以下代碼,我們創(chuàng)建了 RESTful API listUsers,用于讀取用戶的信息列表, server.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,'127.0.0.1',function () {
var host = server.address().address
var port = server.address().port
console.log("應(yīng)用實例,訪問地址為 http://%s:%s", host, port)
})
在瀏覽器中訪問 http://127.0.0.1:8081/listUsers,結(jié)果如下所示:
添加用戶
以下代碼,我們創(chuàng)建了 RESTful API addUser, 用于添加新的用戶數(shù)據(jù),server.js 文件代碼如下所示:
var express = require('express');
var app = express();
var fs = require("fs");
//添加的新用戶數(shù)據(jù)
var user = {
"user4" : {
"name" : "mohit",
"password" : "password4",
"profession" : "teacher",
"id": 4
}
}
app.get('/addUser', function (req, res) {
// 讀取已存在的數(shù)據(jù)
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,'127.0.0.1',function () {
var host = server.address().address
var port = server.address().port
console.log("應(yīng)用實例,訪問地址為 http://%s:%s", host, port)
})
顯示用戶詳情
我們創(chuàng)建了 RESTful API :id(用戶id), 用于讀取指定用戶的詳細信息,server.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,'127.0.0.1',function () {
var host = server.address().address
var port = server.address().port
console.log("應(yīng)用實例,訪問地址為 http://%s:%s", host, port)
})
刪除用戶
我們創(chuàng)建了 RESTful API deleteUser, 用于刪除指定用戶的詳細信息,以下實例中,用戶 id 為 2,server.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" + id];
console.log( data );
res.end( JSON.stringify(data));
});
})
var server = app.listen(8081,'127.0.0.1',function () {
var host = server.address().address
var port = server.address().port
console.log("應(yīng)用實例,訪問地址為 http://%s:%s", host, port)
})
多進程
Node 提供了 child_process 模塊來創(chuàng)建子進程,方法有:
exec() 方法
讓我們創(chuàng)建兩個 js 文件 support.js 和 master.js。
support.js 文件代碼:
console.log("進程 " + process.argv[2] + " 執(zhí)行。" );
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);
});
}
柚子快報激活碼778899分享:Node.js---菜鳥教程
好文鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。