使用node搭建一个可在任何目录下通过命令启动的一个简单http静态服务器
安装:npm install yg-server -g
启动:yg-server
可通过以上命令安装,启动,来看一下最终的效果
TODO
创建一个静态服务器
通过yargs来创建命令行工具
处理缓存
处理压缩
初始化
创建目录:mkdir static-server
进入到该目录:cd static-server
初始化项目:npm init
构建文件夹目录结构:
初始化静态服务器
首先在src目录下创建一个app.js
引入所有需要的包,非node自带的需要npm安装一下
初始化构造函数,options参数由命令行传入,后续会讲到
this.host 主机名
this.port 端口号
this.rootPath 根目录
this.cors 是否开启跨域
this.openbrowser 是否自动打开浏览器
const http = require('http'); // http模块 const url = require('url'); // 解析路径 const path = require('path'); // path模块 const fs = require('fs'); // 文件处理模块 const mime = require('mime'); // 解析文件类型 const crypto = require('crypto'); // 加密模块 const zlib = require('zlib'); // 压缩 const openbrowser = require('open'); //自动启动浏览器 const handlebars = require('handlebars'); // 模版 const templates = require('./templates'); // 用来渲染的模版文件 class StaticServer { constructor(options) { this.host = options.host; this.port = options.port; this.rootPath = process.cwd(); this.cors = options.cors; this.openbrowser = options.openbrowser; } }
处理错误响应
在写具体业务前,先封装几个处理响应的函数,分别是错误的响应处理,没有找到资源的响应处理,在后面会调用这么几个函数来做响应
处理错误
返回状态码500
返回错误信息
responseError(req, res, err) { res.writeHead(500); res.end(`there is something wrong in th server! please try later!`); }
处理资源未找到的响应
返回状态码404
返回一个404html
responseNotFound(req, res) { // 这里是用handlerbar处理了一个模版并返回,这个模版只是单纯的一个写着404html const html = handlebars.compile(templates.notFound)(); res.writeHead(404, { 'Content-Type': 'text/html' }); res.end(html); }
处理缓存
在前面的一篇文章里我介绍过node处理缓存的几种方式,这里为了方便我只使用的协商缓存,通过ETag来做验证
cacheHandler(req, res, filepath) { return new Promise((resolve, reject) => { const readStream = fs.createReadStream(filepath); const md5 = crypto.createHash('md5'); const ifNoneMatch = req.headers['if-none-match']; readStream.on('data', data => { md5.update(data); }); readStream.on('end', () => { let etag = md5.digest('hex'); if (ifNoneMatch === etag) { resolve(true); } resolve(etag); }); readStream.on('error', err => { reject(err); }); }); }
处理压缩
通过请求头accept-encoding来判断浏览器支持的压缩方式
设置压缩响应头,并创建对文件的压缩方式
compressHandler(req, res) { const acceptEncoding = req.headers['accept-encoding']; if (/\bgzip\b/.test(acceptEncoding)) { res.setHeader('Content-Encoding', 'gzip'); return zlib.createGzip(); } else if (/\bdeflate\b/.test(acceptEncoding)) { res.setHeader('Content-Encoding', 'deflate'); return zlib.createDeflate(); } else { return false; } }
启动静态服务器
添加一个启动服务器的方法
所有请求都交给this.requestHandler这个函数来处理
监听端口号
start() { const server = http.createSercer((req, res) => this.requestHandler(req, res)); server.listen(this.port, () => { if (this.openbrowser) { openbrowser(`${this.host}:${this.port}`); } console.log(`server started in ${this.host}:${this.port}`); }); }
请求处理
通过url模块解析请求路径,获取请求资源名
获取请求的文件路径
通过fs模块判断文件是否存在,这里分三种情况
请求路径是一个文件夹,则调用responseDirectory处理
请求路径是一个文件,则调用responseFile处理
如果请求的文件不存在,则调用responseNotFound处理
requestHandler(req, res) { // 通过url模块解析请求路径,获取请求文件 const { pathname } = url.parse(req.url); // 获取请求的文件路径 const filepath = path.join(this.rootPath, pathname); // 判断文件是否存在 fs.stat(filepath, (err, stat) => { if (!err) { if (stat.isDirectory()) { this.responseDirectory(req, res, filepath, pathname); } else { this.responseFile(req, res, filepath, stat); } } else { this.responseNotFound(req, res); } }); }
处理请求的文件
每次返回文件前,先调用前面我们写的cacheHandler模块来处理缓存
如果有缓存则返回304
如果不存在缓存,则设置文件类型,etag,跨域响应头
调用compressHandler对返回的文件进行压缩处理
返回资源