應用node搭建簡易靜態服務

對於http的瞭解,之前只看下200還有404之類。稍微學習nodehttp後發現了不少平時應用的到但不易察覺的功能。 好比請求響應中的壓縮,圖片防盜鏈的應用等等。瞭解到應用node核心模塊http等能夠搭建本地服務,小試一下~javascript

如今,就利用http搭建一個小巧的本地服務。css

建立文件基本目錄

|--my-server
|  |--bin
|  |  |--www.js
|  |--src 
|  |   |--config.js
|  |   |--index.js
|  |   |--template.html
|  |--public
|      |--index.html
|      |--index.css
|      |--a
|         |--test.txt
複製代碼

起步

  • www.js 作入口html

    #! /usr/bin/env node
    require('../src/index')
    複製代碼

    #! /usr/bin/env node爲指定腳本的解釋程序。java

    這種寫法主要是爲了讓你的程序在不一樣的系統上都能適用。 #!/usr/bin/env node會自動的在你的用戶PATH變量中所定義的目錄中尋找node來執行的。node

  • index.js 爲項目主要內容,http在此引用並搭建服務。git

    let http = require('http')
    http.createServer(function(req,res){
      res.end('hello world')
    }).listen(3000)
    複製代碼

綁定命令

npm link 會綁定package.jsonbin下的命令。docker

{
 "bin": {
    "run-server": "bin/www.js"
  }
}
複製代碼

執行npm link結果,顯示命令成功以www.js爲入口:npm

up to date in 0.143s
/usr/local/bin/run-server -> /usr/local/lib/node_modules/http-jyn-static/bin/www.js /usr/local/lib/node_modules/http-jyn-static -> /Users/jyn/work/study/june/static_server/jyn_server
複製代碼

此時進行以下操做測試:json

  1. 執行 run-server,啓動服務
  2. 瀏覽器請求 localhost://3002
  3. 測試成功,頁面返回hello world

服務實現

添加配置文件config.js:api

module.exports = {
  port: 3002,
  host: 'localhost',
  dir: public
} 
複製代碼

實現打開網頁後能夠將目錄下的內容展現給用戶,還能夠進行對應的點擊操做。

建立serevr

做爲能夠複用的靜態服務,須要將server寫成構造函數類型

class Server{
    constructor(){
        this.config = config
    }
    handleRequest(){
        console.log(this)
    }
    start(){
        let server = http.createServer(this.handleRequest.bind(this))// 讓this指向當前的server
        server.listen(config.port,config.host,()=>{
            console.log(`server start http://${config.host}:${config.port}`)
        })
    }
}
複製代碼

運行結果以下:

/usr/local/bin/node /Users/jyn/work/study/june/static_server/jyn_server/src/index.js
server start http://localhost:3002
複製代碼

說明啓動命令能夠啓動服務。 基本搭建結束,繼續完善:

展現文件目錄

promisify應用

經過promisify方便書寫代碼,處理文件讀取等異步問題

結合promisify,獲取文件信息,可方便後面分析文件路徑,讀取文件等:

let stat = util.promisify(fs.stat);// promisify
let readdir = util.promisify(fs.readdir);
let p = path.join(dir, pathname);
let statObj = await stat(p);
複製代碼

目錄分析

statObj.isDirectory()根據目錄類型還有文件類型進行相應處理; 經過讀取當前訪問的目錄下的全部內容 readdir 數組,而後再把數組渲染回頁面

if (statObj.isDirectory()) {
    res.setHeader('Content-Type', 'text/html;charset=utf8')
    let dirs = await readdir(p);
    dirs = dirs.map(item=>({
    	name:item,
    	href:path.join(pathname,item)
    }))
    res.end(str);
} else {
    this.sendFile(req, res, statObj, p);
}
複製代碼

文件路徑dirs結果爲:

[{ name: 'a', href: '/a' },
 { name: 'test.txt', href: '/test.txt' },
 { name: 'index.css', href: '/index.css' },
 { name: 'index.html', href: '/index.html' } ]
複製代碼

文件路徑問題:

根據文件層級進行相應的拼接,點擊第二層時 須要帶上第一層的路徑,能夠經過直接拼接路徑的方式:

dirs = dirs.map(item=>({
	name:item,
	href:path.join(pathname,item)
}))
複製代碼

此時測試點擊目錄a,顯示結果爲:

[ { name: 'test.txt', href: '/a/test.txt' } ] 
複製代碼

應用ejs模板展現文件目錄

ejs模板參考連接

<% '腳本' 標籤,用於流程控制,無輸出。
%> 通常結束標籤

模板中javascript語句用特殊標籤包裹:

<body>
  <h2><%=name%></h2>
  <%arr.forEach(item=>{%>
    <li><a href="<%=item.href%>"><%=item.name%></a></li>
  <%})%>
</body>
複製代碼

根據ejs模板渲染數據方法ejs.render進行解析,ejs.render()用法爲:

template(data);
// => 輸出繪製後的 HTML 字符串
ejs.render(str, data, options);
// => 輸出繪製後的 HTML 字符串
ejs.renderFile(filename, data, options, function(err, str){
// str => 輸出繪製後的 HTML 字符串
});
複製代碼

應用ejs.render渲染後,設置響應頭爲text/html,讓瀏覽器解析模板字符串並展現

res.setHeader('Content-Type', 'text/html;charset=utf8')
let str = ejs.render(this.template, {
                name: `Index of ${pathname}`,
                arr: dirs
            });
res.end(str);
複製代碼

效果如圖:

此時訪問目錄下文件列表已經成功,下一步須要根據點擊進行文件返回或其餘處理。

返回文件內容

返回文件的內容須要根據文件的不一樣類型設置返回頭,文件類型可應用第三方模塊mime, 經過mimelite.getType(...);獲取並讀取文件內容:

res.setHeader('Content-Type', mime.getType(p) + ';charset=utf8');
fs.createReadStream(p).pipe(res);
複製代碼

實現啓動命令後自動啓動端口

根據node核心模塊 os, 根據不一樣的操做系統實現不一樣的打開方式:

The os.platform() method returns a string identifying the operating system platform as set during compile time of Node.js.

www.js中添加

let os = require('os');
let {exec} = require('child_process')
if(os.platform() === 'win32'){
  exec(`start http://${commander.host}:${commander.port}`);
}else{
  exec(`open http://${commander.host}:${commander.port}`);
}
複製代碼

此時操做之須要一步執行 run-server,啓動服務就好了,這時就會看到瀏覽器自動打開了localhost://3002.

優化

基本功能實現後,能夠對請求添加緩存機制,文件請求格式gzip壓縮等功能。比較簡單,會在下篇關於請求頭響應頭應用的文章中做介紹。

發包

  • npm adduser --輸入用戶,密碼,郵箱
  • npm publish 出現錯誤:
npm ERR! publish Failed PUT 403
npm ERR! code E403
npm ERR! no_perms Private mode enable, only admin can publish this module: http-jyn-static
複製代碼

修正方案: npm config set registry http://registry.npmjs.org 以後再執行一遍發包操做,成功:

➜  jyn_server git:(master) npm publish
+ http-jyn-static@1.0.0
複製代碼

下載使用

在一個擁有以下文件的目錄進行靜態服務安裝:

|--test
|      |--index.html
|      |--config.js
|      |--a
|         |--read.txt
複製代碼

安裝

test
npm install http-jyn-static
複製代碼

啓動服務

run-server
server start http://localhost:3002
複製代碼

運行結果: 出現錯誤,由於件config.js中路徑寫死爲public文件夾,修改成: dir: process.cwd()
進行版本升級:

  • npm version minor
  • npm publish

再次更新package進行測試,成功打開文件夾列表:

以上簡易版靜態服務就算完工啦! 固然,咱們還能夠對本服務作緩存策略,以及文件格式壓縮等優化。涉及到響應頭這些基礎應用,我會集中再寫一篇。
目前的工程化項目以及各類腳手架都集成了靜態服務無需再手動搭建靜態服務。這裏的靜態服務能夠做爲了解腳手架的一個熱身運動吧。也能夠提供給作小項目的同窗使用。

歡迎你們批評指正~

Reference:

Author: Yanni Jia
Nickname: 很是兔
Email: 385067638@qq.com

相關文章
相關標籤/搜索