应用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

相关文章
相关标签/搜索