Node.js核心入門(二)

目錄: Node.js核心入門(一)php

  • 全局對象
  • 經常使用工具
  • 事件機制

Node.js核心入門(二)html

  • 文件系統訪問
  • HTTP服務器與客戶端

文件系統 fs

fs 模塊是文件操做的封裝,它提供了文件的讀取、寫入、改名、刪除、遍歷目錄、連接等 POSIX 文件系統操做,且全部的方法都有異步和同步的形式。異步方法的最後一個參數都是一個回調函數。傳給回調函數的參數取決於具體方法,但回調函數的第一個參數都會保留給異常。若是操做成功完成,則第一個參數會是 null 或 undefined。node

const fs = require('fs');
fs.unlink('/tmp/hello', (err) => {
  if (err) throw err;
  console.log('成功刪除 /tmp/hello');
});

複製代碼

當使用同步方法時,任何異常都會被當即拋出。可使用 try/catch 來處理異常,或讓異常向上冒泡。後端

const fs = require('fs');
fs.unlinkSync('/tmp/hello');
console.log('成功刪除 /tmp/hello');

複製代碼

1.fs.readFile(path,[options], callback)

fs.readFile(path,[options], callback) 是最簡單的讀取。它接受一個必選參數filename,表示要讀取的文件名。第二個參數options是可選的,表示文件的字符編碼。callback是回調函數,用於接收文件的內容。若是不指定options,則 callback 就是第二個參數。回調函數提供兩個參數 err和data,err表示有沒有錯誤發生,data是文件內容。若是指定了options, data 是一個解析後的字符串,不然data將會是以Buffer形式表示的二進制數據。例如:數組

fs.readFile('/etc/passwd', 'utf8', callback);
複製代碼

須要注意的是,當path是一個目錄時,fs.readFile()與fs.readFileSync()的行爲與平臺有關。在 macOS、Linux 與Windows上,會返回一個錯誤。在 FreeBSD 上,會返回目錄內容的表示。緩存

// 在 macOS、Linux 與 Windows 上:
fs.readFile('<directory>', (err, data) => {
  // => [Error: EISDIR: illegal operation on a directory, read <directory>]
});

//  在 FreeBSD 上:
fs.readFile('<directory>', (err, data) => {
  // => null, <data>
});
複製代碼

2.fs.readFileSync(path[, options])

fs.readFileSync(filename,[encoding])是fs.readFile 同步的版本。它接受的參數和fs.readFile相同,但讀取到的文件內容會以函數返回值的形式返回。若是有錯誤發生,fs 將會拋出異常,這時候咱們就須要使用 try 和catch 捕捉並處理異常。bash

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

fs.open(path, flags[, mode], callback)是POSIX open 函數的 封裝,與 C 語言標準庫中的fopen函數相似。它接受兩個必選參數,path 爲文件的路徑,而flags 能夠是如下值:服務器

'r' - 以讀取模式打開文件。若是文件不存在則發生異常。

'r+' - 以讀寫模式打開文件。若是文件不存在則發生異常。

'rs+' - 以同步讀寫模式打開文件。命令操做系統繞過本地文件系統緩存。

(這對 NFS 掛載模式下打開文件頗有用,由於它可讓你跳過潛在的舊本地緩存。 它對 I/O 的性能有明顯的影響,因此除非須要,不然不要使用此標誌。

注意,這不會使 fs.open() 進入同步阻塞調用。 若是那是你想要的,則應該使用 fs.openSync()。)

'w' - 以寫入模式打開文件。文件會被建立(若是文件不存在)或截斷(若是文件存在)。

'wx' - 相似 'w',但若是 path 存在,則失敗。

'w+' - 以讀寫模式打開文件。文件會被建立(若是文件不存在)或截斷(若是文件存在)。

'wx+' - 相似 'w+',但若是 path 存在,則失敗。

'a' - 以追加模式打開文件。若是文件不存在,則會被建立。

'ax' - 相似於 'a',但若是 path 存在,則失敗。

'a+' - 以讀取和追加模式打開文件。若是文件不存在,則會被建立。

'ax+' - 相似於 'a+',但若是 path 存在,則失敗。
複製代碼

mode 可設置文件模式(權限和 sticky 位),但只有當文件被建立時纔有效。默認爲 0o666,可讀寫。app

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

fs.read(fd, buffer, offset, length, position, callback) 是 POSIX read 函數的封裝,相比 fs.readFile 提供了更底層的接口。從fd指定的文件中讀取數據。buffer 是數據將被寫入到的 buffer。offset是buffer 中開始寫入的偏移量。length是一個整數,指定要讀取的字節數。position指定從文件中開始讀取的位置。若是position爲null,則數據從當前文件讀取位置開始讀取,且文件讀取位置會被更新。若是position爲一個整數,則文件讀取位置保持不變。回調有三個參數 (err, bytesRead, buffer)。異步

var fs = require('fs');
fs.open('content.txt', 'r', function(err, fd) {
if (err) {
console.error(err);
return;
}
var buf = new Buffer(8);
fs.read(fd, buf, 0, 8, null, function(err, bytesRead, buffer) {
if (err) {
console.error(err);
return;
}
console.log('bytesRead: ' + bytesRead);
console.log(buffer);
})
});
複製代碼

輸出:

bytesRead: 8
<Buffer 54 65 78 74 20 e6 96 87>
複製代碼

HTTP服務器與客戶端

Node.js 標準庫提供了http模塊,其中封裝了一個高效的 HTTP 服務器和一個簡易的HTTP 客戶端。 http.Server 是一個基於事件的 HTTP 服務器,它的核心由 Node.js 下層 C++ 部分實現,而接口由 JavaScript 封裝,兼顧了高性能與簡易性。 http.request則是一個HTTP 客戶端工具,用於向 HTTP 服務器發起請求,例如實現 Pingback或者內容抓取。

Node.js 中的HTTP接口被設計成支持協議的許多特性。好比,大塊編碼的消息。這些接口不緩衝完整的請求或響應,用戶可以以流的形式處理數據。HTTP消息頭由一個對象表示,其中鍵名是小寫的,鍵值不能修改:

{ 'content-length': '123',
  'content-type': 'text/plain',
  'connection': 'keep-alive',
  'host': 'mysite.com',
  'accept': '*/*' }
複製代碼

爲了支持各類可能的 HTTP 應用,Node.js的 HTTP API是很是底層的。它只涉及流處理與消息解析。它把一個消息解析成消息頭和消息主體,但不解析具體的消息頭或消息主體。鍵名是小寫的,鍵值不能修改。爲了支持各類可能的 HTTP 應用,Node.js 的 HTTP API 是很是底層的。 它只涉及流處理與消息解析。 它把一個消息解析成消息頭和消息主體,但不解析具體的消息頭或消息主體。

HTTP服務器

http.Server 是 http 模塊中的 HTTP 服務器對象,用 Node.js 作的全部基於 HTTP 協議的系統,如網站、社交應用甚至代理服務器,都是基於http.Server實現的。它提供了一套封裝級別很低的API,僅僅是流控制和簡單的學習解析,而全部的高級功能都是經過它的接口來實現的。好比官網上的這個例子:

const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
})
複製代碼

在這段代碼中,就使用了http.createServer([requestListener])來新建一個的 http.Server 實例。如今就先讓咱們來看看http.createServer([requestListener])吧。

1. http.Server 的事件

http.Server 是一個基於事件的 HTTP 服務器,全部的請求都被封裝爲獨立的事件,開發者只須要對它的事件編寫響應函數便可實現 HTTP 服務器的全部功能。它繼承自 EventEmitter ,提供瞭如下幾個事件:

  • request:每次接收到一個請求時觸發。 注意,每一個鏈接可能有多個請求(在 HTTP keep-alive 鏈接的狀況下)。
  • connection :當一個新的 TCP 流被創建時觸發。socket 是一個 net.Socket 類型的對象。 一般用戶無需訪問該事件。 注意,由於協議解析器綁定到 socket 的方式,socket 不會觸發 'readable' 事件。socket 也能夠經過 request.connection 訪問。
  • connect:每當客戶端發送 HTTP CONNECT 請求時觸發。 若是該事件未被監聽,則發送 CONNECT 請求的客戶端會關閉鏈接。當該事件被觸發後,請求的 socket 上沒有 'data' 事件監聽器,這意味着須要綁定 'data' 事件監聽器,用來處理 socket 上被髮送到服務器的數據。
  • close:當服務器關閉時,該事件被觸發。注意不是在用戶鏈接斷開時,而是服務器關閉時。

在這些事件最經常使用的是request是最經常使用的,所以 http 提供了一個捷徑: http.createServer([requestListener]) ,功能是建立一個 HTTP 服務器並將requestListener 做爲 request 事件的監聽函數。咱們上面那個官網的例子就是如此,其實它顯式的實現方法是這樣的:

//httpserver.js
const http = require('http');
const hostname = '127.0.0.1';
const port = 3000;
const server = new http.Server();
server.on('request', (req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});
server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
})
複製代碼

2. http.ServerRequest

http.ServerRequest 是 HTTP 請求的信息,是後端開發者最關注的內容。它通常由http.Server 的 request 事件發送,做爲第一個參數傳遞,一般簡稱 request 或 HTTP 請求通常能夠分爲兩部分:請求頭(Request Header)和請求體(Requset Body)。以上內容因爲長度較短均可以在請求頭解析完成後當即讀取。而請求體可能相對較長,須要必定的時間傳輸,所以 http.ServerRequest 提供瞭如下3個事件用於控制請求體傳輸。req。HTTP請求通常能夠分爲兩部分:請求頭(RequestHeader)和請求體(RequsetBody)。以上內容因爲長度較短均可以在請求頭解析完成後當即讀取。而請求體可能相對較長,須要必定的時間傳輸,所以http.ServerRequest提供瞭如下3個事件用於控制請求體傳輸。http.ServerRequest提供了3個事件用於控制請求體傳輸:

  1. data:當請求體數據到來時,該事件被觸發,提供一個參數給回調函數,是接受到的數據,該事件可能被屢次調用(全部data按順序的集合,是請求體數據)。若是該事件沒有被監聽,請求體將被拋棄;

  2. end:當請求體數據完成時該事件觸發。此後再也不觸發data事件;

  3. close:用戶當前請求結束時,該事件被觸發。不一樣於end,若是用戶強制終止了傳輸,也仍是調用close。

    表4-2  ServerRequest 的屬性
         名 稱                  含 義
         complete        客戶端請求是否已經發送完成
         httpVersion     HTTP 協議版本,一般是 1.0 或 1.1
         method          HTTP 請求方法,如 GET、POST、PUT、DELETE 等
         url             原始的請求路徑,例如 /static/image/x.jpg 或 /user?name=byvoid
         headers         HTTP 請求頭
         trailers        HTTP 請求尾(不常見)
         connection      當前 HTTP 鏈接套接字,爲 net.Socket 的實例
         socket          connection 屬性的別名
         client          client 屬性的別名
    複製代碼

3. 獲取 GET 請求內容

注意, http.ServerRequest 提供的屬性中沒有相似於 PHP 語言中的 _GET 或_POST 的屬性,GET請求被直接內嵌在路徑中。URL是完整的請求路徑(包括?後面的部分),所以手動解析後面的內容做爲GET請求的參數。Node.js的url模塊中的parse函數提供了這個功能。

以url:http://127.0.0.1/user?name=byvoid&email=byvoid@byvoid.com爲例:

var http = require("http");
var url = require("url");
var server = new http.Server();
server.on("request", function (req, res) {
    if (req.url == "/favicon.ico") {
        return;
    }
    var m = url.parse(req.url, true);
    console.log(m)
    res.writeHead(200, {'Content-type': 'text/html;charset = utf8'});
    res.end();
})
server.listen(80);
console.log("The server begin");
複製代碼

console.log輸出內容:

Url {
  protocol: null,
  slashes: null,
  auth: null,
  host: null,
  port: null,
  hostname: null,
  hash: null,
  search:'?name=byvoid&email=byvoid@byvoid.com',
  query: { name: 'byvoid', email:'byvoid@byvoid.com' },
  pathname: '/user',
  path:'/user?name=byvoid&email=byvoid@byvoid.com',
  href:'/user?name=byvoid&email=byvoid@byvoid.com' 
}
複製代碼

4. 獲取 POST 請求內容

HTTP 協議1.1版本提供了8種標準的請求方法,而其中最多見的就是 GET 和 POST。相比GET請求把全部的內容編碼到訪問路徑中,POST 請求的內容所有都在請求體中。http.ServerRequest 並無一個屬性內容是在請求體中,緣由是等待請求體傳輸多是一件耗時的工做,譬如上傳文件。而不少時候咱們可能並不須要理會請求體的內容,且惡意的 POST 請求會大大消耗服務器的資源。因此 Node.js 默認是不會解析請求體的,所以當咱們須要的時候,咱們就要手寫一個,具體實現方法以下:

var http = require('http');
var querystring = require('querystring');
var util = require('util');
http.createServer(function(req, res) {
    var post = '';
    req.on('data', function(chunk) {
    post += chunk;
});
req.on('end', function() {
    post = querystring.parse(post);
    res.end(util.inspect(post));
    });
}).listen(3000);
複製代碼

5.http.ServerResponse

http.ServerResponse 是返回給客戶端的信息,決定了用戶最終能看到的結果。它也是由 http.Server 的 request 事件發送的,做爲第二個參數傳遞,通常簡稱爲 response 或 res 。http.ServerResponse 有三個重要的成員函數,用於返回響應頭、響應內容以及結束請求:

  • response.writeHead(statusCode, [headers]) :向請求的客戶端發送響應頭。statusCode是HTTP狀態碼,如200(請求成功)、404(未找到)等。headers是一個相似關聯數組的對象,表示響應頭的每一個屬性。該函數在一個請求內最多隻能調用一次,若是不調用,則會自動生成一個響應頭。
  • response.write(data, [encoding]) :向請求的客戶端發送響應內容。 data 是一個 Buffer 或字符串,表示要發送的內容。若是 data 是字符串,那麼須要指定 encoding 來講明它的編碼方式,默認是 utf-8 。在 response.end 調用以前,response.write 能夠被屢次調用。
  • response.end([data], [encoding]) :結束響應,告知客戶端全部發送已經完成。當全部要返回的內容發送完畢的時候,該函數 必須 被調用一次。它接受兩個可選參數,意義和 response.write 相同。若是不調用該函數,客戶端將永遠處於等待狀態。

HTTP 客戶端

http 模塊提供了兩個函數 http.request和http.get,功能是做爲客戶端向HTTP服務器發起請求。

1.http.request(options,callback)

http.request(options,callback)發起HTTP請求,它接受兩個參數,option是一個相似關聯數組的對象,表示請求的參數,callback是請求的回調函數。option經常使用的參數以下所示:

  • host :請求網站的域名或 IP 地址。
  • port :請求網站的端口,默認 80。
  • method :請求方法,默認是 GET。
  • path :請求的相對於根的路徑,默認是「 / 」。 QueryString 應該包含在其中。例如 /search?query=byvoid 。
  • headers :一個關聯數組對象,爲請求頭的內容。

而callback 則傳遞一個參數,爲 http.ClientResponse 的實例。http.request 返回一個http.ClientRequest 的實例,下面是一個經過 http.request 發送 POST 請求的代碼:

var http = require('http');
var querystring = require('querystring');
var contents = querystring.stringify({
    name: 'byvoid',
    email: 'byvoid@byvoid.com',
    address: 'Zijing 2#, Tsinghua University',
});
var options = {
    host: 'www.byvoid.com',
    path: '/application/node/post.php',
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length' : contents.length
    }
};
var req = http.request(options, function(res) {
    res.setEncoding('utf8');
    res.on('data', function (data) {
    console.log(data);
    });
});
req.write(contents);
req.end();
複製代碼

運行結果以下:

array(3) {
["name"]=>
string(6) "byvoid"
["email"]=>
string(17) "byvoid@byvoid.com"
["address"]=>
string(30) "Zijing 2#, Tsinghua University"
}
複製代碼

2.http.get(options, callback)

http 模塊還提供了一個更加簡便的方法用於處理GET請求:http.get(options, callback)。它是http.request的簡化版,惟一的區別在於http.get自動將請求方法設爲了 GET 請求,同時不須要手動調用 req.end() :

var http = require('http');
http.get({host: 'www.byvoid.com'}, function(res) {
    res.setEncoding('utf8');
    res.on('data', function (data) {
    console.log(data);
    });
});
複製代碼

http.ClientRequest

該對象在 http.request() 內部被建立並返回。它表示着一個正在處理的請求,其請求頭已進入隊列。它提供一個response事件,即http.request或http.get第二個參數指定的回調函數的綁定對象。

var http = require('http');
var req = http.get({host: 'www.byvoid.com'});
    req.on('response', function(res) {
    res.setEncoding('utf8');
    res.on('data', function (data) {
    console.log(data);
    });
});
複製代碼

http.ClientRequest像http.ServerResponse同樣也提供了 write 和 end 函數,用於向服務器發送請求體,一般用於 POST、PUT 等操做。全部寫結束之後必須調用end函數以通知服務器,不然請求無效。http.ClientRequest 還提供瞭如下經常使用的函數:

  • request.abort() :標記請求爲終止。 調用該方法將使響應中剩餘的數據被丟棄且 socket 被銷燬。
  • request.setTimeout(timeout,[callback]):設置請求超時時間, timeout爲毫秒數。一旦socket被分配給請求且已鏈接,socket.setTimeout() 會被調用。
  • request.end([data[, encoding]][, callback])結束髮送請求。若是部分請求主體還未被髮送,則會刷新它們到流中。 若是請求是分塊的,則會發送終止字符 '0\r\n\r\n'。

http.ClientResponse

http.ClientResponse 與 http.ServerRequest類似,提供了三個事件data、end和 close,分別在數據到達、傳輸結束和鏈接結束時觸發,其中data 事件傳遞一個參數chunk,表示接收到的數據。

http.ClientResponse 也提供了一些屬性,用於表示請求的結果狀態:

statusCode   HTTP 狀態碼,如 200、40四、500
httpVersion  HTTP 協議版本,一般是 1.0 或 1.1
headers      HTTP 請求頭
trailers     HTTP 請求尾(不常見)
複製代碼

http.ClientResponse 還提供瞭如下幾個特殊的函數:

  • response.setEncoding([encoding]):設置默認的編碼,當data事件被觸發時,數據將會以encoding編碼。默認值是null,即不編碼,以Buffer的形式存儲。經常使用編碼爲 utf8。
  • response.pause():暫停接收數據和發送事件,方便實現下載功能。
  • response.resume():從暫停的狀態中恢復。
相關文章
相關標籤/搜索