node 靜態伺服(搭建服務)

基本功能

不急着寫下第一行代碼,而是先梳理一下就基本功能而言有哪些步驟。html

  1. 在本地根據指定端口啓動一個http server,等待着來自客戶端的請求
  2. 當請求抵達時,根據請求的url,以設置的靜態文件目錄爲base,映射獲得文件位置
  3. 檢查文件是否存在
  4. 若是文件不存在,返回404狀態碼,發送not found頁面到客戶端
  5. 若是文件存在:
    • 打開文件待讀取
    • 設置response header
    • 發送文件到客戶端
  6. 等待來自客戶端的下一個請求

實現基本功能

代碼結構

建立一個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()方法會被調用。

相關文章
相關標籤/搜索