不急着寫下第一行代碼,而是先梳理一下就基本功能而言有哪些步驟。html
建立一個nodejs-static-webserver
目錄,在目錄內運行npm init
初始化一個package.json文件。node
mkdir nodejs-static-webserver && cd "$_" // initialize package.json npm init
接着建立以下文件目錄:web
-- config ---- default.json -- static-server.js -- app.js
default.jsonnpm
{ "port": 9527, "root": "/Users/sheila1227/Public", "indexPage": "index.html" }
default.js
存放一些默認配置,好比端口號、靜態文件目錄(root)、默認頁(indexPage)等。當這樣的一個請求http://localhost:9527/myfiles/
抵達時. 若是根據root
映射後獲得的目錄內有index.html,根據咱們的默認配置,就會給客戶端發回index.html的內容。json
static-server.js瀏覽器
const http = require('http'); const path = require('path'); const config = require('./config/default'); class StaticServer { constructor() { this.port = config.port; this.root = config.root; this.indexPage = config.indexPage; } start() { http.createServer((req, res) => { const pathName = path.join(this.root, path.normalize(req.url)); res.writeHead(200); res.end(`Requeste path: ${pathName}`); }).listen(this.port, err => { if (err) { console.error(err); console.info('Failed to start server'); } else { console.info(`Server started on port ${this.port}`); } }); } } module.exports = StaticServer;
在這個模塊文件內,咱們聲明瞭一個StaticServer
類,並給其定義了start
方法,在該方法體內,建立了一個server
對象,監聽rquest
事件,並將服務器綁定到配置文件指定的端口。在這個階段,咱們對於任何請求都暫時不做區分地簡單地返回請求的文件路徑。path
模塊用來規範化鏈接和解析路徑,這樣咱們就不用特地來處理操做系統間的差別。服務器
app.jsapp
const StaticServer = require('./static-server'); (new StaticServer()).start();
在這個文件內,調用上面的static-server
模塊,並建立一個StaticServer實例,調用其start
方法,啓動了一個靜態資源服務器。這個文件後面將不須要作其餘修改,全部對靜態資源服務器的完善都發生在static-server.js
內。less
在目錄下啓動程序會看到成功啓動的log:函數
> node app.js
Server started on port 9527
在瀏覽器中訪問,能夠看到服務器將請求路徑直接返回了。
以前咱們對任何請求都只是向客戶端返回文件位置而已,如今咱們將其替換成返回真正的文件:
routeHandler(pathName, req, res) { } start() { http.createServer((req, res) => { const pathName = path.join(this.root, path.normalize(req.url)); this.routeHandler(pathName, req, res); }).listen(this.port, err => { ... }); }
將由routeHandler
來處理文件發送。
讀取文件以前,用fs.stat
檢測文件是否存在,若是文件不存在,回調函數會接收到錯誤,發送404響應。
respondNotFound(req, res) { res.writeHead(404, { 'Content-Type': 'text/html' }); res.end(`<h1>Not Found</h1><p>The requested URL ${req.url} was not found on this server.</p>`); } respondFile(pathName, req, res) { const readStream = fs.createReadStream(pathName); readStream.pipe(res); } routeHandler(pathName, req, res) { fs.stat(pathName, (err, stat) => { if (!err) { this.respondFile(pathName, req, res); } else { this.respondNotFound(req, res); } }); }
Note:
讀取文件,這裏用的是流的形式
createReadStream
而不是readFile
,是由於後者會在獲得完整文件內容以前將其先讀到內存裏。這樣萬一文件很大,再趕上多個請求同時訪問,readFile
就承受不來了。使用文件可讀流,服務端不用等到數據徹底加載到內存再發回給客戶端,而是一邊讀一邊發送分塊響應。這時響應裏會包含以下響應頭:Transfer-Encoding:chunked
默認狀況下,可讀流結束時,可寫流的
end()
方法會被調用。