一 实现的功能
该项目是一个可以在任意目录下启动的一个静态文件服务器,可以把当前目录做为静态文件根目录。该静态文件服务器实现的功能有:
- 显示目录下面的文件列表和返回内容
- 实现缓存
- 实现压缩的功能
- 获取部分数据
二 如何实现
1 yargs实现自动化
首先在package.json中配置bin:
"bin": {
"mystatic": "bin/static"
}
复制代码
这样就可以直接在命令行执行bin目录下的mystatic文件了:
$ mystatic
复制代码
而且我们可以在命令行里配置参数,我们使用yargs模块。yargs模块为命令行参数解析工具,可以将命令行里的配置信息转化为对象。
$ mystatic -d/--root 指定静态文件根目录 -p/--port 指定端口号 -o/--host 指定监听的主机
复制代码
2 代码的实现
let config = require('./config');
let http = require('http');
let chalk = require('chalk');
let path = require('path');
let url = require('url');
let fs = require('fs');
let zlib = require('zlib');
let handlebars = require('handlebars');
let {promisify, inspect} = require('util');
let mime = require('mime');
let stat = promisify(fs.stat);
let readdir = promisify(fs.readdir);
let debug = require('debug')('static:app');
//编译模板,得到一个渲染的方法,然后传入实际数据数据就可以得到渲染后的HTML
function list() {
let tmpl = fs.readFileSync(path.resolve(__dirname, 'template', 'list.html'), 'utf8');
return handlebars.compile(tmpl);
}
// 创建一个Server 类
class Server {
constructor(argv) {
this.list = list();
this.config = Object.assign({}, config, argv);
}
start() {
let server = http.createServer();
server.on('request', this.request.bind(this));
server.listen(this.config.port, () => {
let url = `http://${this.config.host}:${this.config.port}`;
debug(`server started at ${chalk.green(url)}`);
});
}
//静态文件服务器
async request(req, res) {
let {pathname} = url.parse(req.url);
if (pathname == '/favicon.ico') {
return this.sendError('not found', req, res);
}
let filepath = path.join(this.config.root, pathname);
try {
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;charset=utf8');
res.end(html);
} else {
this.sendFile(req, res, filepath, statObj);
}
} catch (e) {
debug(inspect(e));
this.sendError(e, req, res);
}
}
sendFile(req, res, filepath, statObj) {
res.setHeader('Content-Type', mime.getType(filepath) + ';charset=utf-8');
res.setHeader('Content-Encoding', 'gzip');
// 是否缓存
if (this.handleCache(req, res, filepath, statObj)) return; //如果走缓存,则直接返回
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);
}
}
// 实现缓存
handleCache(req, res, filepath, statObj) {
let ifModifiedSince = req.headers['if-modified-since'];
let isNoneMatch = req.headers['if-none-match'];
let etag = statObj.size;
res.setHeader('ETag', etag);
let lastModified = statObj.ctime.toGMTString();
res.setHeader('Last-Modified', lastModified);
res.setHeader('Cache-Control', 'private,max-age=30');
res.setHeader('Expires', new Date(Date.now() + 30 * 1000).toGMTString());
if ((ifModifiedSince && ifModifiedSince == lastModified) && (isNoneMatch && isNoneMatch == etag)) {
res.writeHead(304);
res.end();
return true;
} else {
return false;
}
}
// 实现压缩
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;
}
}
// 实现部分内容的返回
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
});
}
sendError(err, req, res) {
res.statusCode = 500;
res.end(`${err.toString()}`);
}
}
module.exports = Server;
复制代码
更多细节请查看我的GitHub mystatic,欢迎star
3 发布到npm
$ npm publish
复制代码
npm上直接搜索mystatic-now
即可。
三 如何使用
- 1 安装
$ npm install mystatic-now -g
复制代码
- 2 在所需要查看的文件目录下启动命令行,输入
$ mystatic
复制代码
- 3 在浏览器中输入
localhost:8080/
即可查看。