如何搭一个静态服务

  1. 新建一个文件夹
  2. 初始化 npm init -y
  3. 所用到的模块
  • 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搭一个静态服务相关推荐

  1. node创建web静态服务

    在上一篇中,用了http模块和fs模块来让html页面呈现出来,但是这样做有缺点,比如我们来看一个例子,例如我现在手上有一个前端写好了的静态文件夹,我想吧它部署在node的静态服务上,你将会发现,如果 ...

  2. 原生node写一个静态资源服务器

    myanywhere 用原生node做一个简易阉割版的anywhere静态资源服务器,以提升对node与http的理解. 相关知识 es6及es7语法 http的相关网络知识 响应头 缓存相关 压缩相 ...

  3. 用原生Node实现一个静态web服务器

    之前一直用过Apache nginx等静态web服务器. 但强大的node.js本身就能作为独立的web服务器,不依赖与Apache  nginx 下面我们看看怎么用Node去写一个静态服务器吧 首先 ...

  4. Node --- 构建一个HTTP服务

    代码如下: var http = require('http'); http.createServer(function (req, res){res.writeHead(200,{'Content- ...

  5. Nodejs中搭建一个静态Web服务器,通过读取文件获取响应类型

    场景 Web服务器一般指网站服务器,是指驻留于因特网上某种类型计算机的程序,可以向浏览器等Web客户端提供文档,也可以放置网站文件让全世界浏览,还可以放置数据文件,让全世界下载.目前最主流的Web服务 ...

  6. 如何在Linux开启HTTP服务,小技巧:如何快速开启一个静态 HTTP 服务?

    静态 HTTP 服务的几个用途: 静态网页的 HTTP 服务,以访问浏览 如:生成的文档.博客等 公开文件的 HTTP 服务,以访问下载 如:分享的文档.安装包等 以下会介绍目前我了解的方式中,最推荐 ...

  7. 为一个 iOS 应用编写一个简单的 Node.js/MongoDB Web 服务

    原文链接:https://github.com/nixzhu/dev-blog/blob/master/2014-04-21-write-a-simple-nodejs-mongodb-web-ser ...

  8. express如何返回一个html文档,node.js express 返回一个静态页面

    首先我们要有一个静态页面 .html 文件. 然后,我们初始化一个express 项目. 首先新建一个目录,进入它,如果没有下载express的话,使用命名下载npm install express ...

  9. 用 TypeScript 编写一个 React 服务端渲染库(1)

    前言 代码都甩在 Github 上面了,欢迎随手 star ? 踩坑的过程大概都在 TypeScript + Webpack + Koa 搭建 React 服务端渲染 这篇文章里面 踩坑的 DEMO ...

最新文章

  1. 零基础python从入门到精通 pdf-100G Python从入门到精通全套资料!
  2. 升级 90天 vs2008 在win2008下。
  3. 简单的bean分页输出
  4. 青少年蓝桥杯_2020_steam考试_初级组_第四题
  5. java 搜索文件 pdf_Java查找并高亮PDF文本过程解析
  6. svg配合css3动画_带有Adobe Illustrator,HTML和CSS的任何网站的SVG动画
  7. 特斯拉部分车型将逐渐搭载磷酸铁锂电池,比亚迪有望成为其电池供应商
  8. html运行显示无法发布,我无法发表文章当我打开发表文章,总在网页的左下角出现网页发生错误 爱问知识人...
  9. 阿里火力全开 IoT!
  10. 全球编程厉害的14位大佬
  11. 查看Tensorflow SavedModel模型信息
  12. macbook linux 双显卡,网友支招:苹果笔记本也能双显卡切换
  13. PHP中文转换为数组,PHP文本处理之中文汉字字符串转换为数组
  14. 用java开发一个简单的安卓程序,Android NDK开发简单程序分享(Hello Word!)
  15. vim normal 模式、插入模式、命令行模式
  16. jmeter中的响应断言
  17. 英语语法之强调句和倒装
  18. Android中Parcel的解读
  19. RestSharp 使用(含超时处理)
  20. Fast RCNN论文学习

热门文章

  1. 利用 Java dump 进行 JVM 故障诊断
  2. Java虚拟机详解05----垃圾收集器及GC参数
  3. Windows下Eclipse+PyDev安装Python开发环境
  4. 程序员面试题精选100题(16)-O(logn)求Fibonacci数列[算法]
  5. C语言小知识点练习总结
  6. STL 的string类怎么啦?
  7. boost源码剖析之:boost::multi_array
  8. MATLAB语言中的关系与逻辑运算
  9. 软件构造第三章 第五部分
  10. P2221 [HAOI2012]高速公路