天天看點

Node.js 寫一個簡單的靜态伺服器 4 使用模闆引擎傳回一個html 檔案

好的,前面我們寫了一個簡單到不可用的功能。現在,我們實作一下,傳回的是一個友好的界面,如果傳回值中有路徑的話,那麼它是可以點選并檢視的。

那我們就需要模闆引擎,這兒,我們使用 EJS 

首先,我們安裝 EJS https://github.com/mde/ejs , https://ejs.bootcss.com/

npm install ejs

安裝好了後,我們就開始寫代碼了。

我們先來建立一個目錄 src/templates。 我們把所有的模闆檔案都放這兒。在這個目錄下建立檔案main_page.ejs

<!DOCTYPE html>
<html >
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        a {
            display: block;
        }
    </style>
</head>
<body>
    <% files.map( file => { %>
        <a href="<%= dir %>/<%= file %>" target="_blank" rel="external nofollow" > <%= file %> </a>
    <% }) %>
</body>
</html>
           

我們進入src/helper 下的route.js ,先引用一下 ejs 。除了require 裡,代碼裡我們一般還是使用絕對路徑。下面我們先擷取模闆并解析。雖然是用同步的檔案讀取方法,但隻會執行一次(以後可以從緩存中擷取)并且後面函數依賴這個檔案。是以用的是同步的方法。

const fs = require('fs');
const path = require('path');
const ejs = require('ejs');
const promisify = require('util').promisify;
const conf = require('../config/defaultConfig');

const stat = promisify(fs.stat);
const readdir = promisify(fs.readdir);

const ejsPath = path.join(__dirname, '../templates/main-page.ejs');
const source = fs.readFileSync(ejsPath,'utf-8');

module.exports = async function (req, res, filePath) {
    try {
        const stats = await stat(filePath);
        if (stats.isFile()) {
          res.statusCode = 200;
          res.setHeader('Content-Type', 'text/plain');
          fs.createReadStream(filePath).pipe(res)
        }
        if (stats.isDirectory()) {
          const files = await readdir(filePath);
          res.statusCode = 200;
          res.setHeader('Content-Type', 'text/html');
          const data = {
            title: path.basename(filePath),
            dir: path.relative(conf.root, filePath),
            files
          };
          res.end(ejs.render(source, data));
        }
      } catch(ex) {
        res.statusCode = 404;
        res.setHeader('Content-Type', 'text/plain');
        res.end(`${filePath} is not a file or directory`);
      }
}
           

上面的代碼,表面上是好的,但是在點入多層目錄後,會有問題。是路徑的問題。我們改成下面這樣就好。

const fs = require('fs');
const path = require('path');
const ejs = require('ejs');
const promisify = require('util').promisify;
const conf = require('../config/defaultConfig');

const stat = promisify(fs.stat);
const readdir = promisify(fs.readdir);

const ejsPath = path.join(__dirname, '../templates/main-page.ejs');
const source = fs.readFileSync(ejsPath,'utf-8');

module.exports = async function (req, res, filePath) {
    try {
        const stats = await stat(filePath);
        if (stats.isFile()) {
          res.statusCode = 200;
          res.setHeader('Content-Type', 'text/plain');
          fs.createReadStream(filePath).pipe(res)
        }
        if (stats.isDirectory()) {
          const files = await readdir(filePath);
          res.statusCode = 200;
          res.setHeader('Content-Type', 'text/html');
          const dir = path.relative(conf.root, filePath)
          const data = {
            title: path.basename(filePath),
            dir: dir ? `/${dir}` : '',
            files
          };
          res.end(ejs.render(source, data));
        }
      } catch(ex) {
        res.statusCode = 404;
        res.setHeader('Content-Type', 'text/plain');
        res.end(`${filePath} is not a file or directory`);
      }
}
           

Done.

繼續閱讀