node静态服务器tudo
实现以下功能
- 读取静态文件或目录
- MIME类型支持
- 缓存支持/控制
- 支持gzip压缩
- 访问目录可以自动寻找下面的index.html文件
- Range支持,断点续传
- 图片防盗链
- 后台运行
【node静态服务器tudo】基本功能读取静态文件此功能分为两部分:
* 1、返回文件
调用fs建立可读流读取文件返回客户端
fs.createReadStream(filepath).pipe(res);
* 2、返回目录
因为是要返回当前请求路径下的所有目录,并且支持点击进入下一级,所有返回文件为html
采用handlebars模板引擎编辑模板// 处理模板函数
let list = function () {
let template = fs.readFileSync(listTemplatePath, 'utf8');
return handlebars.compile(template);
}// template 文件
{{title}} - 锐客网
{{#each files}}
-
{{name}}
{{/each}}
// 读取当前路径下的所有目录
let { promisify, inspect } = util;
// 转变fs.readdir 异步方法返回promise
let readdirPromise = promisify(fs.readdir);
// 返回filepath下所有目录
let files = await readdirPromise(filepath)
// 处理files为我们想要的数据
files = files.map(file => ({
name: file,
url: path.join(pathname, file),
}))// 最终返回客户端的html
// list
let html = this.list({
title: pathname,
files,
});
MIME类型支持
res.setHeader('Content-Type', mime.getType(filepath));
缓存支持/控制
toCache (req, res, filepath, stat) {
// 强制缓存
res.setHeader('Cache-Control', 'private,max-age=60');
// http 1.1
// private 客户端可以缓存
// public 客户端和代理服务器都可以缓存
// max-age=60 缓存内容将在60秒后失效
// no-cache 需要使用对比缓存验证数据,强制向源服务器再次验证
// no-store 所有内容都不会缓存,强制缓存和对比缓存都不会触发
res.setHeader('Expires', new Date(Date.now() + 60 * 1000).toUTCString());
// http 1.0// 对比缓存
// last-modify
let ifModifiedSince = req.headers['if-modified-since'];
let lastModified = stat.ctime.toGMTString();
// etag
let ifNoneMatch = req.headers['if-none-match'];
let eTag = cypto.createHash('sha1').update(stat.ctime.toGMTString() + stat.size).digest('hex');
if (ifModifiedSince || ifNoneMatch) {
if (ifNoneMatch === eTag && lastModified === ifModifiedSince) {
res.statusCode = 304;
res.end('');
return false;
}
if ((ifModifiedSince && lastModified === ifModifiedSince) || (ifNoneMatch && ifNoneMatch === eTag)) {
res.statusCode = 304;
res.end('');
return false;
}
}
res.setHeader('Last-Modified', lastModified);
res.setHeader('ETag', eTag);
return true;
}
压缩支持
async gzip(req, res, gzipInfo) {
let encoding = req.headers['accept-encoding'];
let gzipReg = /\bgzip\b/;
let deflateReg = /\bdeflate\b/;
let type, streamInfo;
if (gzipReg.test(encoding)) {
streamInfo = gzipInfo ? await gzipPromise(gzipInfo) : zlib.createGzip();
type = 'gzip';
} else if (deflateReg.test(encoding)) {
streamInfo = gzipInfo ? await deflatePromise(gzipInfo) : zlib.createDeflate();
type = 'deflate';
}
if (type) {
res.setHeader('Content-Encoding', type);
}
return streamInfo;
}
访问目录可以自动寻找下面的index.html文件
let indexFilepath = path.join(filepath, '/', 'index.html');
try {
let statIndex = await statPromise(indexFilepath);
if (statIndex) {
// 返回 inde.html文件
return this.sendFile(req, res, indexFilepath, stat);
}
} catch (e) {}
Range支持,断点续传
range (req, res, filepath, stat) {
res.setHeader('Accept-Range', 'bytes');
// 通知客户端支持获取部分资源
let range = req.headers['range'];
// Range: bytes=0-xxx
let start = 0, end = stat.size;
if (range) {
let result = range.match(/bytes=(\d*)-(\d*)/);
start = isNaN(result[1]) ? result[1] : start;
end = isNaN(result[2]) ? result[2] : end;
}
return fs.createReadStream(filepath, {
start,
end,
})
}
图片防盗链
notSteal (req, res) {
console.log(path.join(process.cwd(), 'imgs/load.png'))
let refer = req.headers['referer'] || req.headers['refer'];
//如果说有refer的话,则表示是从HTML页面中引用过来的
if (refer) {
let { host } = Url.parse(refer);
if (host !== hostDomain) {
// 返回默认图
return fs.createReadStream(path.join(process.cwd(), 'imgs/load.png'));
}
}
}
开启子进程运行
let { spawn } =require('child_process');
let fs = require('fs');
let serverPath = '../src/child.js';
let child = spawn('node', [serverPath], {
detached: true,
stdio: ['ignore', process.stdout, 'ignore']
})child.unref();
- 功能尚不完善,之后会慢慢丰富!
- 更多细节源码请查看我的GitHub sevenStatic
参考
- Node.js静态文件服务器实战
推荐阅读
- Node.js中readline模块实现终端输入
- 探索免费开源服务器tomcat的魅力
- [源码解析]|[源码解析] NVIDIA HugeCTR,GPU版本参数服务器---(3)
- iOS,打Framework静态库
- java静态代理模式
- Android|Android JNI之静态注册(android studio)
- mac|mac 链接linux服务器 如何在Mac上连接服务器
- Linux|Linux 服务器nginx相关命令
- 运维|如何限制IP 通过 SSH连接服务器
- Promise|Promise 异步控制流