用node搭一个静态服务
如何搭一个静态服务
- 新建一个文件夹
- 初始化
npm init -y
- 所用到的模块
http-server
起服务mime chalk debug ejs
所需模块http fs util path
自带模块yargs
目录结构
启动一个服务需要 src/congfig文件
- 运行的条件 指定主机名
- 指定启动的端口号
- 自定运行的目录
通过config
配置 app
起服务 tepl
编译 bin和package.json
链接命令行
配置
config.js
//将配置挂载在我们的实例上,方便后期手动指定,直接get就可以
let path = require('path')
let config = {hostname : '127.0.0.1', //主机port:3000, //端口号dir:path.join(__dirname, 'public')
}
module.exports = config;
复制代码
tmpl
<!--ejs模版,当是文件夹的时候会用到-->
<body><%dirs.map(item=>{%><li><a href="<%=item.pathname%>"><%=item.filename%></a></li> <%})%>
</body>
复制代码
配置全局管理 (全局映射关系)
package.json
//添加bin配置
"bin": {
"zdl": "bin/www.js"
},
复制代码
执行npm link
bin/www.js 用此文件作为入口,帮我们引用app.js
#! /usr/bin/env node
//yargs用法 在命令行输入`zdl --help`测试是否生效
// 第一执行了命令后 会执行 bin/www.js
let yargs = require('yargs')
let argv = yargs.option('port', {alias: 'p',default: 3000, //默认demand: false, //是否必填description: 'this is port'
}).option('hostname', {alias: 'host',default: 'localhost',type: String, //类型demand: false,description: 'this is hostname'
}).option('dir', {alias: 'd',default: process.cwd(),type: String,demand: false,description: 'this is cwd'
}).usage('zdl [options]').argv; //用法提示
//用此文件作为入口,帮我们引用app.js
let Server = require('../src/app');
new Server(argv).start(); // 开启服务
//相当于在app.js执行
//let server = new Server(argv);
//server.start();
复制代码
app.js
架子
// 实现一个静态服务
let http = require('http');
let fs = require('fs');
let url = require('url');
let util = require('util');
let path = require('path');let mime = require('mime');
let ejs = require('ejs'); // 渲染模板
let chalk = require('chalk'); // 粉笔
let debug = require('debug')('*');//所有的都输出
// 第二个参数可以指定环境变量在为什么值时才打印
// window set DEBUG=XXX export DEBUG=XXXXclass Server {constructor(args) { //config在调用此文件时传参argsconstructor(args) {this.config = {...config,...args},this.template = template;//渲染模版}async handleRequest(req, res) { // 这里的this都是实例let { pathname } = url.parse(req.url, true);let p = path.join(this.config.dir, pathname);try{let statObj = await stat(p);if (statObj.isDirectory()) {//若是文件夹直接渲染数据let dirs = await readdir(p);dirs = dirs.map(dir => { return {filename: dir, pathname: path.join(pathname, dir)}}); let str = ejs.render(this.template, { dirs, title: 'ejs' });//template渲染模板,数据,渲染结果是字符串res.setHeader('Content-Type', 'text/html;charset=utf8');res.end(str);} else {// 文件 发送文件this.sendFile(req, res, p, statObj);}} catch (e) {// 文件不存在的情况this.sendError(req, res, e);}}//创建服务start() {let server = http.createServer(this.handleRequest.bind(this));//this.handleRequest,this是回调函数的this,也可以在该函数return 箭头函数let { hostname, port } = this.config;debug(`http://${hostname}:${chalk.green(port)} start`)server.listen(port, hostname);}//错误处理sendError(){// 解析字符串打印对象//debug(util.inspect(e).toString());res.statusCode = 404;res.end(`Not Found`);}//发送文件sendFile(){...}
}module.exports = Server;
复制代码
添加功能
添加的三个功能都是http的应用,想更具体请参考node~http缓存
class Server {// 添加缓存功能呢cache(req, res, p, stat) {}//添加压缩功能gzip(req, res, p, stat) {}//添加范围请求功能range(req, res, p, stat) {}//如果是文件sendFile(req, res, p, stat) {}
}
复制代码
如果是目录 就将目录中的内容展现出来. 如果是文件就将文件展示出来
cache
// 添加缓存功能呢cache(req, res, p, stat) { //Catch-Control Expries if-modified-since if-none-matchlet since = req.headers['if-modified-since'];let match = req.headers['if-none-match'];let ssince = stat.ctime.toUTCString();let smatch = stat.ctime.getTime() + stat.size;res.setHeader('Last-Modified', ssince);res.setHeader('ETag', smatch);res.setHeader('Cache-Control', 'max-age=6');if (since != ssince) {debug(since, ssince);return false;}if (match != smatch) {debug(match, smatch);return false}return true;}//如果是文件sendFile(req, res, p, stat) {if (this.cache(req, res, p, stat)) {// 检测是否有缓存res.statusCode = 304;res.end();return;}res.setHeader('Content-Type', mime.getType(p) + ';charset=utf8');fs.createReadStream(p, { start, end }).pipe(res);}
复制代码
gzip
//添加压缩功能gzip(req, res, p, stat) {let header = req.headers['accept-encoding'];if (header) {if (header.match(/\bgzip\b/)) {res.setHeader('Content-Encoding', 'gzip')return zlib.createGzip();} else if (header.match(/\bdeflate\b/)) {res.setHeader('Content-Encoding', 'deflate')return zlib.createDeflate();}} else {return false;}}//如果是文件sendFile(req, res, p, stat) {...let compress = this.gzip(req, res, p, stat);if (compress) { // 返回的是一个压缩流res.setHeader('Content-Type', mime.getType(p) + ';charset=utf8');fs.createReadStream(p).pipe(compress).pipe(res);} else {res.setHeader('Content-Type', mime.getType(p) + ';charset=utf8');fs.createReadStream(p).pipe(res);}}
复制代码
range
//添加范围请求功能range(req, res, p, stat) {let range = req.headers['range'];if(range){let [, start, end] = range.match(/(\d*)-(\d*)/) || [];start = start ? parseInt(start) : 0;end = end ? parseInt(end) : stat.size;res.statusCode = 206;res.setHeader('Accept-Ranges', 'bytes');res.setHeader('Content-Length', end - start + 1);res.setHeader('Content-Range', `bytes ${start}-${end}/${stat.size}`);return { start, end };}else{return {start:0,end:stat.size}}}//如果是文件sendFile(req, res, p, stat) {let { start, end } = this.range(req, res, p, stat);if (compress) { // 返回的是一个压缩流res.setHeader('Content-Type', mime.getType(p) + ';charset=utf8');fs.createReadStream(p, { start, end }).pipe(compress).pipe(res);} else {res.setHeader('Content-Type', mime.getType(p) + ';charset=utf8');fs.createReadStream(p, { start, end }).pipe(res);}}
复制代码
小结
以下是整个app.js的内容
// 实现一个静态服务
// 如果是目录 就将目录中的内容展现出来
// 如果是文件就将文件展示出来
let http = require('http');
let fs = require('fs');
let url = require('url');
let zlib = require('zlib');
let util = require('util');
let path = require('path');let mime = require('mime');
let ejs = require('ejs'); // 渲染模板
let chalk = require('chalk'); // 粉笔
// 第二个参数可以指定环境变量在为什么值时才打印
// window set DEBUG=XXX export DEBUG=XXXX
let debug = require('debug')('*');
// 运行的条件 指定主机名
// 指定启动的端口号
// 自定运行的目录
let stat = util.promisify(fs.stat);
let readdir = util.promisify(fs.readdir);
let config = require('./config');
let template = fs.readFileSync(path.resolve(__dirname, 'tmpl.html'), 'utf8');
class Server {constructor(args) {this.config = { ...config, ...args };// 将配置挂载在我们的实例上this.template = template;}async handleRequest(req, res) { // 这里的this都是实例let { pathname } = url.parse(req.url, true);let p = path.join(this.config.dir, pathname);// 1.根据路径 如果是文件夹 显示文件夹里的内容// 2.如果是文件 显示文件的内容try { // 如果没错误说明文件存在let statObj = await stat(p);if (statObj.isDirectory()) {// 现在需要一个当前目录下的解析出的对象或者数组let dirs = await readdir(p);dirs = dirs.map(dir => { // dirs就是要渲染的数据return {filename: dir,pathname: path.join(pathname, dir)}});let str = ejs.render(this.template, { dirs, title: 'ejs' });res.setHeader('Content-Type', 'text/html;charset=utf8');res.end(str);} else {// 文件 发送文件this.sendFile(req, res, p, statObj);}} catch (e) {// 文件不存在的情况this.sendError(req, res, e);}}// 实现其他功能// 实现范围请求// 实现缓存// 服务器 Cache-Control Expires // Last-Modified ETag:ctime+size// 客户端// if-modified-since if-none-matchcache(req, res, p, stat) {// 实现缓存 let since = req.headers['if-modified-since'];let match = req.headers['if-none-match'];let ssince = stat.ctime.toUTCString();let smatch = stat.ctime.getTime() + stat.size;res.setHeader('Last-Modified', ssince);res.setHeader('ETag', smatch);res.setHeader('Cache-Control', 'max-age=6');if (since != ssince) {debug(since, ssince);return false;}if (match != smatch) {debug(match, smatch);return false}return true;}// 实现服务端压缩gzip(req, res, p, stat) {let header = req.headers['accept-encoding'];if (header) {if (header.match(/\bgzip\b/)) {res.setHeader('Content-Encoding', 'gzip')return zlib.createGzip();} else if (header.match(/\bdeflate\b/)) {res.setHeader('Content-Encoding', 'deflate')return zlib.createDeflate();}} else {return false;}}range(req, res, p, stat) {let range = req.headers['range'];if(range){let [, start, end] = range.match(/(\d*)-(\d*)/) || [];start = start ? parseInt(start) : 0;end = end ? parseInt(end) : stat.size;res.statusCode = 206;res.setHeader('Accept-Ranges', 'bytes');res.setHeader('Content-Length', end - start + 1);res.setHeader('Content-Range', `bytes ${start}-${end}/${stat.size}`);return { start, end };}else{return {start:0,end:stat.size}}}sendFile(req, res, p, stat) {if (this.cache(req, res, p, stat)) {// 检测是否有缓存res.statusCode = 304;res.end();return};let compress = this.gzip(req, res, p, stat);let { start, end } = this.range(req, res, p, stat); //范围请求,返回开始位置和结束位置if (compress) { // 返回的是一个压缩流res.setHeader('Content-Type', mime.getType(p) + ';charset=utf8');fs.createReadStream(p, { start, end }).pipe(compress).pipe(res);} else {res.setHeader('Content-Type', mime.getType(p) + ';charset=utf8');fs.createReadStream(p, { start, end }).pipe(res);}}sendError(req, res, e) {// 解析字符串打印对象//debug(util.inspect(e).toString());res.statusCode = 404;res.end(`Not Found`);}start() {let server = http.createServer(this.handleRequest.bind(this));let { hostname, port } = this.config;debug(`http://${hostname}:${chalk.green(port)} start`)server.listen(port, hostname);}
}
// 开启一个服务
module.exports = Server
复制代码
现在你只需要在cmd里面输入zdl就可以启动一个静态服务了
发布到npm
发包
在用的时候我们可以直接 npm install [name]
然后zdl
运行
用node搭一个静态服务相关推荐
- node创建web静态服务
在上一篇中,用了http模块和fs模块来让html页面呈现出来,但是这样做有缺点,比如我们来看一个例子,例如我现在手上有一个前端写好了的静态文件夹,我想吧它部署在node的静态服务上,你将会发现,如果 ...
- 原生node写一个静态资源服务器
myanywhere 用原生node做一个简易阉割版的anywhere静态资源服务器,以提升对node与http的理解. 相关知识 es6及es7语法 http的相关网络知识 响应头 缓存相关 压缩相 ...
- 用原生Node实现一个静态web服务器
之前一直用过Apache nginx等静态web服务器. 但强大的node.js本身就能作为独立的web服务器,不依赖与Apache nginx 下面我们看看怎么用Node去写一个静态服务器吧 首先 ...
- Node --- 构建一个HTTP服务
代码如下: var http = require('http'); http.createServer(function (req, res){res.writeHead(200,{'Content- ...
- Nodejs中搭建一个静态Web服务器,通过读取文件获取响应类型
场景 Web服务器一般指网站服务器,是指驻留于因特网上某种类型计算机的程序,可以向浏览器等Web客户端提供文档,也可以放置网站文件让全世界浏览,还可以放置数据文件,让全世界下载.目前最主流的Web服务 ...
- 如何在Linux开启HTTP服务,小技巧:如何快速开启一个静态 HTTP 服务?
静态 HTTP 服务的几个用途: 静态网页的 HTTP 服务,以访问浏览 如:生成的文档.博客等 公开文件的 HTTP 服务,以访问下载 如:分享的文档.安装包等 以下会介绍目前我了解的方式中,最推荐 ...
- 为一个 iOS 应用编写一个简单的 Node.js/MongoDB Web 服务
原文链接:https://github.com/nixzhu/dev-blog/blob/master/2014-04-21-write-a-simple-nodejs-mongodb-web-ser ...
- express如何返回一个html文档,node.js express 返回一个静态页面
首先我们要有一个静态页面 .html 文件. 然后,我们初始化一个express 项目. 首先新建一个目录,进入它,如果没有下载express的话,使用命名下载npm install express ...
- 用 TypeScript 编写一个 React 服务端渲染库(1)
前言 代码都甩在 Github 上面了,欢迎随手 star ? 踩坑的过程大概都在 TypeScript + Webpack + Koa 搭建 React 服务端渲染 这篇文章里面 踩坑的 DEMO ...
最新文章
- 零基础python从入门到精通 pdf-100G Python从入门到精通全套资料!
- 升级 90天 vs2008 在win2008下。
- 简单的bean分页输出
- 青少年蓝桥杯_2020_steam考试_初级组_第四题
- java 搜索文件 pdf_Java查找并高亮PDF文本过程解析
- svg配合css3动画_带有Adobe Illustrator,HTML和CSS的任何网站的SVG动画
- 特斯拉部分车型将逐渐搭载磷酸铁锂电池,比亚迪有望成为其电池供应商
- html运行显示无法发布,我无法发表文章当我打开发表文章,总在网页的左下角出现网页发生错误 爱问知识人...
- 阿里火力全开 IoT!
- 全球编程厉害的14位大佬
- 查看Tensorflow SavedModel模型信息
- macbook linux 双显卡,网友支招:苹果笔记本也能双显卡切换
- PHP中文转换为数组,PHP文本处理之中文汉字字符串转换为数组
- 用java开发一个简单的安卓程序,Android NDK开发简单程序分享(Hello Word!)
- vim normal 模式、插入模式、命令行模式
- jmeter中的响应断言
- 英语语法之强调句和倒装
- Android中Parcel的解读
- RestSharp 使用(含超时处理)
- Fast RCNN论文学习
热门文章
- 利用 Java dump 进行 JVM 故障诊断
- Java虚拟机详解05----垃圾收集器及GC参数
- Windows下Eclipse+PyDev安装Python开发环境
- 程序员面试题精选100题(16)-O(logn)求Fibonacci数列[算法]
- C语言小知识点练习总结
- STL 的string类怎么啦?
- boost源码剖析之:boost::multi_array
- MATLAB语言中的关系与逻辑运算
- 软件构造第三章 第五部分
- P2221 [HAOI2012]高速公路