利用node.js构建静态服务器,让你理解底层的服务器是怎样实现的,让你对服务器有一个更好的认识。
实现了如下功能:
- 读取静态文件
- MIME类型支持
- 缓存支持/控制
- 支持gzip压缩
- 只能访问指定目录, 不能访问指定目录的上级目录,保证安全
- 访问目录可以自动寻找下面的index.html文件
- Range支持,断点续传
- 发布为可执行命令并可以后台运行,可以通过npm install -g安装
读取静态文件
let { promisify, inspect } = require('util');let stat = promisify(fs.stat);let readdir = promisify(fs.readdir);let statObj = await stat(filepath);if (statObj.isDirectory()) {//如果是目录的话,应该显示目录 下面的文件列表 let files = await readdir(filepath); files = files.map(file => ({ name: file, url: path.join(pathname, file) })); let html = this.list({ title: pathname, files }); res.setHeader('Content-Type', 'text/html'); res.end(html);} else { this.sendFile(req, res, filepath, statObj);}sendFile(req, res, filepath, statObj) { if (this.handleCache(req, res, filepath, statObj)) return; //如果走缓存,则直接返回 res.setHeader('Content-Type', mime.getType(filepath) + ';charset=utf-8');// .jpg let encoding = this.getEncoding(req, res); let rs = this.getStream(req, res, filepath, statObj); if (encoding) { rs.pipe(encoding).pipe(res); } else { rs.pipe(res); }}getStream(req, res, filepath, statObj) { let start = 0; let end = statObj.size - 1; let range = req.headers['range']; return fs.createReadStream(filepath, { start, end });}复制代码
MiMe类型检测
这个很简单,对应的代码是:
let mime = require('mime');res.setHeader('Content-Type', mime.getType(filepath) + ';charset=utf-8');// .jpg复制代码
缓存支持/控制, 对应的代码是:
if (this.handleCache(req, res, filepath, statObj)) return; //如果走缓存,则直接返回handleCache(req, res, filepath, statObj) { let ifModifiedSince = req.headers['if-modified-since']; let isNoneMatch = req.headers['is-none-match']; res.setHeader('Cache-Control', 'private,max-age=30'); res.setHeader('Expires', new Date(Date.now() + 30 * 1000).toGMTString()); let etag = statObj.size; let lastModified = statObj.ctime.toGMTString(); res.setHeader('ETag', etag); res.setHeader('Last-Modified', lastModified); if (isNoneMatch && isNoneMatch != etag) { return fasle; } if (ifModifiedSince && ifModifiedSince != lastModified) { return fasle; } if (isNoneMatch || ifModifiedSince) { res.writeHead(304); res.end(); return true; } else { return false; }}复制代码
支持gzip压缩
getEncoding(req, res) { //Accept-Encoding:gzip, deflate let 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 null; }}if (encoding) { rs.pipe(encoding).pipe(res);} else { rs.pipe(res);}复制代码
只能访问指定目录, 不能访问指定目录的上级目录,保证安全
假如一个同学用浏览器访问http://localhost:8000/../xxx.js 怎么办捏?浏览器会自动干掉那两个作为父路径的点的。浏览器会把这个路径组装成http://localhost:8000/xxx.js
但是curl命令可以: curl -i http://localhost:8000/../xxx.js
那么怎么办呢?
var realPath = path.join("assets", path.normalize(pathname.replace(/\.\./g, "")));复制代码
强制替换即可。
Range支持,断点续传
getStream(req, res, filepath, statObj) { let start = 0; let end = statObj.size - 1; let range = req.headers['range']; if (range) { res.setHeader('Accept-Range', 'bytes'); res.statusCode = 206;//返回整个内容的一块 let result = range.match(/bytes=(\d*)-(\d*)/); if (result) { start = isNaN(result[1]) ? start : parseInt(result[1]); end = isNaN(result[2]) ? end : parseInt(result[2]) - 1; } } return fs.createReadStream(filepath, { start, end });}复制代码
*** 发布为可执行命令并可以后台运行,可以通过npm install -g安装
在package中添加:
"bin": { "studytest": "bin/www" },复制代码
执行 npm link 这个时候其实就可以全局执行了
最后发布
- npm adduser
- npm publish