Nodejs http (二)

前言

全局安装 http-server npm install http-server -g
http-server 以当前文件夹为根目录的服务被启动

Server服务

现在手写下这个服务
这里要做的功能是仿 http-server 启动服务 浏览器能够访问当前文件夹下的启动的这个服务

基本步骤:

初始化

npm init -y
有一个执行命令的脚本 用于解析用户的参数 新建bin/www文件 便于记忆 有病的环境

1
2
3
// bin/www
#! /usr/bin/env node
console.log('create server');

配脚本

package.json文件添加如下配置:

映射到全局

iTerm cd到当前目录17.http/ npm link

可以看到全局命令下有个server命令,执行它就会执行bin/www

拓展功能

- -help

基本上写一个命令行工具都会有这样一个功能 server - -help
这里会用到这个比较流行的工具 commander
npm install commander 解析命令行参数的,只要写工具,必备的包。
先可以参考下http-server的- -help

-p

-p 改端口号(默认是8080) http-server -p 3000 这样开启的端口就是3000
现在实现跟上面一样的功能 server -p 3000
现在需要用commander去解析命令行工具 现在去www文件

1
2
3
4
5
6
7
8
// www
#! /usr/bin/env node

const commander = require('commander');
// 这个是commonjs规范
// 有时候会这样引入
import commander from 'commander';
// 这里运行一下 server 会报错 因为这是es6语法,node中不支持 这时候需要把es6转成es5 核心就要用到babel包

babel es6 => es5

安装 npm install @babel/cli @babel/core @babel/preset-env --save-dev

@babel/cli (在命令行里面调它,它默认会调用 @babel/core)
@babel/core (babel的核心文件,找配置文件.babelrc,它会根据配置文件去调 @babel/preset-env 这个包)
@babel/preset-env (它的功能就是把es6=>es5)

新建 .babelrc文件

1
2
3
4
5
6
7
8
9
10
{
"presets": [
["@babel/preset-env",{
"targets":{
"node":"current"
}
}]
]
}
// line4-6意思是需要把es6编译到什么地步,这里需要编译到node版本,当前版本, import 编译成 require

下面这个文件的作用只做require      解析参数的功能放到 /src/main.js

1
2
3
4
5
// www
#! /usr/bin/env node

// require('../src/main'); // 这里引得是源代码 es6写的,但是最终需要引的是打包出来的es5,所以变成如下
require('../dist/main');
1
2
3
// main.js
import commander from 'commander'; // 在这里执行解析参数的功能
console.log(commander);

好 现在来解析
可以用 npx babel –help 查看参数 这里npx 会走node_modules/.bin/babel
转化 npx babel src -d dist 解析src文件夹下的内容 放到dist文件夹下

现在 server 就能够获取到编译好的代码 但是每次main.js改动都需要手动执行 现如下
npx babel src -d dist --watch 加一个监控 可以自动编译 这样在node环境就可以用es6语法来写这个功能了

小调整 用npx可能给别人用不太好 package.json增加line11、12

npm run babel:watch      npm run babel

解析参数

1
2
3
4
5
6
7
8
9
10
11
12
// main.js
import pragram from 'commander';

pragram
.option('-p, --port <val>', 'set http-server port')
.parse(process.argv);

let config = {
port: 8080
}
Object.assign(config, pragram);
console.log(pragram.port, config.port);

执行 server –help
设置option参数可以 执行 npm commander 参考
执行 server -p 3000 打印 3000 3000

通过解析的参数启动一个服务

获取当前文件夹下的内容(文件夹、文件),拼接成模板显示出来,如图:

新建server.js

1
2
3
4
// main.js
import Server from './server';
let server = new Server(config);
server.start();
1
2
3
4
5
6
7
8
9
10
// server.js
class Server{
constructor(config){
console.log(config);
}
start(){

}
}
export default Server;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
// server.js
import http from 'http';
import fs, { readFileSync } from 'fs';
import path from 'path';
import util from 'util';
import url from 'url';
import zlib from 'zlib';
import crypto from 'crypto';
let { readFile,writeFile,readdir,stat } = fs.promises
// 第三方
import mime from 'mime';
import chalk from 'chalk'; // 变颜色
import ejs from 'ejs';
// npm i mime chalk ejs

let template = fs.readFileSync(path.resolve(__dirname,'../template.html'),'utf8');
// console.log(template);
class Server{
constructor(config){
this.port = config.port;
this.template = template; // 将template属性放到实例上 可以保证属性永远可以通过this来获取,不被参数覆盖掉
// console.log(config);
}
async handleRequest(req,res){
let {pathname} = url.parse(req.url, true);
// 解析中文路径
pathname = decodeURIComponent(pathname);
// 如果是中文路径 浏览器会把中文转化成十六进制 decodeURIComponent('你好'); 可以先把这行注释了看看能否访问'你好' 文件夹
// let filePath = path.join(__dirname, pathname);
// 这里面启动server时 引用的是dist下的main __dirname 打印 /Users/jiafei/Desktop/Architecture-Course/17.http/dist ,__dirname指当前执行文件的路径,到时候打包到dist文件下, 而这个服务是启动当前文件执行的目录 应该就是17.http目录
let filePath = path.join(process.cwd(), pathname); // 找到当前执行命令的目录
// 找到当前执行命令的目录,并执行,而不是当前文件所在的文件夹目录
// console.log(filePath); // /Users/jiafei/Desktop/Architecture-Course/17.http/

try {
let statObj = await stat(filePath);
if(statObj.isDirectory()){
// console.log('是目录');
// 我需要判断是不是目录
// 如果是目录 需要读取目录下的文件
let dirs = await readdir(filePath);
console.log(dirs);
/*
[ '.babelrc',
'1.md',
'bin',
'dist',
'http-1.md',
'http-2.md',
'http-3.md',
'node_modules',
'package-lock.json',
'package.json',
'src' ]
*/
// 现在需要将这些列表数据渲染成一个页面展示给前端 这里用到ejs 新建template.html 这里将静态资源放在了跟目录下
// 我需要访问文件的时候 增加他当前的路径,如果是/ 就不增加了,否则将路径拼在文件前面
let templateStr = ejs.render(this.template,{
dirs,
path:pathname === '/'?'':pathname
}); // 返回的是渲染好的字符串
res.setHeader('Content-Type','text/html;charset=utf-8')
res.end(templateStr);
}else{
this.sendFile(filePath,req,res,statObj); // 浏览器请求 localhost:8080/http-1.md
}

} catch (error) {
this.sendError(error,req,res);
}

}
sendFile(filePath,req,res,statObj){ // 创建流返回去就好了
let t = require("mime").getType(filePath);
res.setHeader("Content-Type", t + ";charset=utf-8");
fs.createReadStream(filePath).pipe(res);
}
sendError(error,req,res){
console.log(e);
res.statusCode = 404;
res.end('Not Found');
}
start(){
let server = http.createServer(this.handleRequest.bind(this));
server.listen(this.port, ()=>{
// 这一块的参数展现形式包括颜色 参考http-server 图1
console.log(`${chalk.yellow('Starting up http-server, serving')} ${chalk.blue('./')}
Available on:
http://127.0.0.1:${chalk.red('8080')}
http://192.168.0.100::${chalk.yellow('8080')}
Hit CTRL-C to stop the server`);
// 现在已经启动了这服务 现在需要能够访问 => handleRequest()

});
}
}
export default Server;

新建 template.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<ul>
<%dirs.forEach(dir=>{%>
<li><a href="<%=path%>/<%=dir%>"><%=dir%></a></li>
<%})%>
</ul>
</body>
</html>

http优化策略

1) 压缩

客户端说我支持:Accept-Encoding: gzip, deflate, br(这里node支持前2种)

服务器:Content-Encoding: gzip

怎么实现

转化流

流:可读流(源码里面自己实现了一个_read)   可写流(源码里自己实现了一个_write)   双工流_read _write
转化流:压缩就是靠这个转化流实现的

现在自己构建一个转化流

1
2
3
4
5
6
7
8
9
10
const {Transform} = require('stream');

// 自己写个流MyTransform 继承 Transform 再去实现某些方法
class MyTransform extends Transform{
_transform(){

}
}

let myTransform = new MyTransform(); // 创建一个转化流

延伸 插入一个知识点

1
2
3
4
5
6
7
8
9
10
// 标准输出
1. process.stdout.write('ok'); // 可写流 write('ok') 意思就是往控制台里输出 ok 命令行 node 1.stream.js 运行下

2. process.stdin.on('data', function(data) {
process.stdout.write(data);
});
// 描述 将读到的内容data 再写出去 命令行 node 1.stream.js 运行下 输入 123 回车 123
// 分析:
// process.stdin 可读流 process.stdout 可写流 如何将可读流的数据导到可写流中
3. process.stdin.pipe(process.stdout); // 再执行

补gif图
进阶 如果输入 abc 能够输出ABC
中间做一步转化 把读取到的内容先放到一个流里面转化完 再把它写出来

1
2
3
4
5
6
7
8
9
10
11
12
13
const {Transform} = require('stream');

class MyTransform extends Transform{
// _transform 这个方法是源码内部的 和上面的_write _read一样
_transform(chunk,encoding,callback){ // 和可写流一样
chunk = chunk.toString().toUpperCase();
// 再写到读流里
this.push(chunk);
callback(); // 清空缓存 不加的话只能转化一次 再写入就放到缓存里了 这里可以运行试下
}
}
let myTransform = new MyTransform();
process.stdin.pipe(myTransform).pipe(process.stdout);

补gif图

压缩 gzip
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const zlib = require('zlib');
// 新建一个1.txt 文件内容里面重复的越多,压缩的越小
const fs = require('fs');
const path = require('path');

zlib.gzip(fs.readFileSync(path.resolve(__dirname,'1.txt')), function(err,data){
// 整个压缩完之后再写
console.log(data,'---')
fs.writeFileSync(path.resolve(__dirname,'1.txt.gz'),data);
})
// 这里可以看到7k压缩成了145字节
// 上面这种方法是整个读取之后 => 整个压缩 => 整个写入

const zlib = require('zlib');
const fs = require('fs');
const path = require('path');
fs.createReadStream(path.resolve(__dirname,'1.txt')).pipe(zlib.createGzip()).pipe(fs.createWriteStream(path.resolve(__dirname,'2.XTX.GZ')));
// 读取一点 转换一点 写一点 分段 比上面那个的性能好

1.txt

1
1111111111111111111....

压缩是可解压的 大致会压缩成 这里有1百万个1 把重复的替换掉 变得更小

怎么应用

开始写压缩流程
客户端说我支持:Accept-Encoding: gzip, deflate, br 先将服务启动起来 server
访问http-1.md http://127.0.0.1:8080/http-1.md 现在是4.5k 没有压缩

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 如果文件很大 在服务器上压缩会有性能问题
gzip(filePath,req,res,statObj){ // 现在压缩从4.5k => 2.6k
// 查看一下当前是否支持压缩
let encoding = req.headers['accept-encoding'];
if(encoding){ // 查看一下是否支持gzip 、 deflate
if(encoding.match(/gzip/)){
res.setHeader("Content-Encoding", 'gzip'); // 告诉浏览器是用什么压缩的
return zlib.createGzip();
}else if(encoding.match(/deflate/)){
res.setHeader("Content-Encoding", 'deflate');
return zlib.createDeflate();
}
return false;
}
return false;
}
sendFile(filePath,req,res,statObj){ // 创建流返回去就好了
// 在文件发送过程中 如果浏览器支持压缩 我需要对浏览器的内容先压缩 再返回
let flag = this.gzip(filePath,req,res,statObj);
let t = require("mime").getType(filePath) || 'text/plain';
// 这里加了一个默认的文本类型 如果打开的是www 这个没有后缀,mime就不能获取类型了
res.setHeader("Content-Type", t + ";charset=utf-8");
if( !flag){
fs.createReadStream(filePath).pipe(res); // 以前是可读流导入到可写流里
}else{
fs.createReadStream(filePath).pipe(flag).pipe(res);
}
}

2)缓存

强制缓存

先新建 1.html 、1.css 启动服务 server     http://127.0.0.1:8080/1.html

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--1.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<link rel="stylesheet" href="1.css">
</body>
</html>
1
2
3
4
// 1.css
body{
background: red
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// server.js
sendFile(filePath,req,res,statObj){ // 创建流返回去就好了
// 缓存
// 强制缓存:
// 表示10秒内不要再访问服务器
res.setHeader('Cache-Control','max-age=10');
// 老版本写法 新版本也可以生效 如果两个都写 采用Cache-Control
res.setHeader('Expires',new Date(Date.now()+10*1000).toGMTString());
console.log('打印~~', filePath);

let flag = this.gzip(filePath,req,res,statObj);
let t = require("mime").getType(filePath) || 'text/plain';
res.setHeader("Content-Type", t + ";charset=utf-8");
if( !flag){
fs.createReadStream(filePath).pipe(res);
}else{
fs.createReadStream(filePath).pipe(flag).pipe(res);
}
}

现在看看效果 访问 http://127.0.0.1:8080/1.html

如图所示浏览器缓存了 ‘css’

如图所示 css走的缓存
但是这里html还是走的服务器

如果设置强制缓存 首页是不会缓存的 所以首页一定会发起请求,如果首页设置了强制缓存,它包含的资源则会走缓存

1
2
3
// 其他的设置
res.setHeader('Cache-Control','no-cache'); // 每次都访问服务器 但是缓存
res.setHeader('Cache-Control','no-store'); // 不走缓存 而且缓存里没有

对比缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
cache(filePath,req,res,statObj){// 协商缓存/ 对比缓存
// statObj.ctime 是一个时间类型
let lasModified = statObj.ctime.toGMTString();
res.setHeader('Last-Modified',lasModified);
let ifModifiedSince = req.headers['if-modified-since'];

if(ifModifiedSince) {
if(ifModifiedSince !== lasModified){
return false
}
}else{
return false;
}
return true;
}
sendFile(filePath,req,res,statObj){ // 创建流返回去就好了
// 缓存
res.setHeader('Cache-Control','no-cache'); // 每次都访问服务器 但是缓存

// 对比缓存 对比缓存这里将其他缓存设置的第一条打开
let cache = this.cache(filePath,req,res,statObj);
if(cache){ // 有缓存 直接找缓存里的内容就好了
res.statusCode = 304;
return res.end();
}

let flag = this.gzip(filePath,req,res,statObj);
let t = require("mime").getType(filePath) || 'text/plain';
res.setHeader("Content-Type", t + ";charset=utf-8");
if( !flag){
fs.createReadStream(filePath).pipe(res);
}else{
fs.createReadStream(filePath).pipe(flag).pipe(res);
}
}

第一次刷新 res里有这个时间戳

第二次刷新请求,请求头带了这个时间戳

并且这时的状态码是304

  • 存在的问题
    1) 文件可能没发生变化 但是修改时间变化了 (写入新的数据然后又撤销了,这样文件没有被修改,但是修改的那个时间戳改变了)
    2) 精确的时间不准确 会导致问题 同一时间内改了很多次
    3) cdn (静态资源通过cdn cdn分发的时间不一样也会导致缓存失效)

解决方式 : 指纹

指纹

什么叫指纹:把整个内容进行md5,md5之后 内容只要有一点发生改变,md5戳也会发生变化
新建 2.crypto.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 2.crypto.js
const crypto = require('crypto');

// 摘要算法 md5不叫加密算法,因为它不可逆
// md5特点:
// 1)不可逆
// 2) 相同内容 摘要的结果相同,有一点不同出来的结果完全不同
// 3) 摘要的结果长度相同
let r = crypto.createHash('md5').update('1234').update('56').digest('base64');
console.log(r); // 4QrcOUm6Wau+VuBX8g+IPg==
//https://cmd5.com/

// sha1 sha256 加盐算法
const crypto = require('crypto');
let r = crypto.createHmac('sha1','key12345...').update('123456').digest('base64');
console.log(r); // GEVfizKc3KFLfxH1UUlazNNMPlk=
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// server.js
cache(filePath,req,res,statObj){
// 解决方式 : 指纹
let Etag = crypto.createHash('md5').update(fs.readFileSync(filePath)).digest('base64');
res.setHeader('Etag', Etag); // 真的Etag可能只是文件的某一部分
let ifNoneMatch = req.headers['if-none-match'];
if(ifNoneMatch) {
if(ifNoneMatch !== Etag){
return false
}
}else{
return false;
}
return true;
}
// 我们可以计算文件md5戳 文件如果很大会读取前20个 slice(0,20)

完整的server.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// server.js
import http from 'http';
import fs, { readFileSync } from 'fs';
import path from 'path';
import util from 'util';
import url from 'url';
import zlib from 'zlib';
import crypto from 'crypto';
let { readFile,writeFile,readdir,stat } = fs.promises
// 第三方
import mime from 'mime';
import chalk from 'chalk'; // 变颜色
import ejs from 'ejs';
// npm i mime chalk ejs

let template = fs.readFileSync(path.resolve(__dirname,'../template.html'),'utf8');
// console.log(template);
class Server{
constructor(config){
this.port = config.port;
this.template = template; // 将template属性放到实例上 可以保证属性永远可以通过this来获取,不被参数覆盖掉
// console.log(config);
}
async handleRequest(req,res){
let {pathname} = url.parse(req.url, true);
// 解析中文路径
pathname = decodeURIComponent(pathname); // 如果是中文路径 浏览器会把中文转化成十六进制 decodeURIComponent('你好'); 可以先把这行注释了看看能否访问‘你好’ 文件夹
// let filePath = path.join(__dirname, pathname);
// 这里面启动server时 引用的是dist下的main __dirname 打印 /Users/jiafei/Desktop/Architecture-Course/17.http/dist ,__dirname指当前执行文件的路径,到时候打包到dist文件下, 而这个服务是启动当前文件执行的目录 应该就是17.http目录
let filePath = path.join(process.cwd(), pathname); // 找到当前执行命令的目录
// 找到当前执行命令的目录,并执行,而不是当前文件所在的文件夹目录
// console.log(filePath); // /Users/jiafei/Desktop/Architecture-Course/17.http/

try {
let statObj = await stat(filePath);
if(statObj.isDirectory()){
// console.log('是目录');
// 我需要判断是不是目录
// 如果是目录 需要读取目录下的文件
let dirs = await readdir(filePath);
console.log(dirs);
/*
[ '.babelrc',
'1.md',
'bin',
'dist',
'http-1.md',
'http-2.md',
'http-3.md',
'node_modules',
'package-lock.json',
'package.json',
'src' ]
*/
// 现在需要将这些列表数据渲染成一个页面展示给前端 这里用到ejs 新建template.html 这里将静态资源放在了跟目录下
// 我需要访问文件的时候 增加他当前的路径,如果是/ 就不增加了,否则将路径拼在文件前面
let templateStr = ejs.render(this.template,{
dirs,
path:pathname === '/'?'':pathname
}); // 返回的是渲染好的字符串
res.setHeader('Content-Type','text/html;charset=utf-8')
res.end(templateStr);
}else{
this.sendFile(filePath,req,res,statObj); // 浏览器请求 localhost:8080/http-1.md
}
} catch (error) {
this.sendError(error,req,res);
}
}
// 如果文件很大 在服务器上压缩会有性能问题
gzip(filePath,req,res,statObj){ // 现在压缩从4.5k => 2.6k
// 查看一下当前是否支持压缩
let encoding = req.headers['accept-encoding'];
if(encoding){ // 查看一下是否支持gzip 、 deflate
if(encoding.match(/gzip/)){
res.setHeader("Content-Encoding", 'gzip'); // 告诉浏览器是用什么压缩的
return zlib.createGzip();
}else if(encoding.match(/deflate/)){
res.setHeader("Content-Encoding", 'deflate');
return zlib.createDeflate();
}
return false;
}
return false;
}
cache(filePath,req,res,statObj){// 协商缓存/ 对比缓存
// 存在的问题
// 1) 文件可能没发生变化 但是修改时间变化了 (写入新的数据然后又撤销了,这样文件没有被修改,但是修改的那个时间戳改变了)
// 2) 精确的时间不准确 会导致问题 同一时间内改了很多次
// 3) cdn (静态资源通过cdn cdn分发的时间不一样也会导致缓存失效)
// 解决方式 : 指纹
// statObj.ctime 是一个时间类型
let lasModified = statObj.ctime.toGMTString();
let Etag = crypto.createHash('md5').update(fs.readFileSync(filePath)).digest('base64');
res.setHeader('Last-Modified',lasModified);
res.setHeader('Etag', Etag); // 真真的Etag可能只是文件的某一部分
let ifModifiedSince = req.headers['if-modified-since'];
let ifNoneMatch = req.headers['if-none-match'];
// if(ifNoneMatch) {
// if(ifNoneMatch !== Etag){
// return false
// }
// }else{
// return false;
// }
// if(ifModifiedSince) {
// if(ifModifiedSince !== lasModified){
// return false
// }
// }else{
// return false;
// }
// 对比缓存+指纹 有一个不满足就返回false

if(ifNoneMatch && ifModifiedSince){
if(ifNoneMatch !== Etag || ifModifiedSince !== lasModified){
return false
}
return true;
}else{
return false;
}

// 指纹 什么叫指纹:把整个内容进行md5,md5之后 内容只要有一点发生改变,md5戳也会发生变化
// 如果指纹变了 就不是一个人 md5 这里新建 2.crypto.js
// 我们可以计算文件md5戳 文件如果很大会读取前20个 slice(0,20)

}
sendFile(filePath,req,res,statObj){ // 创建流返回去就好了
// 缓存
// 强制缓存:
// 表示10秒内不要再访问服务器
res.setHeader('Cache-Control','max-age=10');
// // 老版本写法 新版本也可以生效 如果两个都写 采用Cache-Control
// res.setHeader('Expires',new Date(Date.now()+10*1000).toGMTString());
// // 现在看看效果 访问 http://127.0.0.1:8080/1.html
// console.log('打印~~', filePath); // 如图’浏览器缓存了css‘ 缓存了css
// 但是这里html还是走的服务器 如果设置强制缓存 首页是不会缓存的 所以首页一定会发起请求,如果首页设置了强制缓存,它包换的资源则会走缓存
// 如图'css走的缓存'

// 其他的设置
//res.setHeader('Cache-Control','no-cache'); // 每次都访问服务器 但是缓存
// res.setHeader('Cache-Control','no-store'); // 不走缓存 而且缓存里没有

// 对比缓存 这里新建副本3 对比缓存这里将其他缓存设置的第一条打开
let cache = this.cache(filePath,req,res,statObj);
if(cache){ // 有缓存 直接找缓存里的内容就好了
res.statusCode = 304;
return res.end();
}

// 在文件发送过程中 如果浏览器支持压缩 我需要对浏览器的内容先压缩 再返回
let flag = this.gzip(filePath,req,res,statObj);

let t = require("mime").getType(filePath) || 'text/plain'; // 这里加了一个默认的文本类型 如果打开的是www 这个没有后缀,mime就不能获取类型了
res.setHeader("Content-Type", t + ";charset=utf-8");
if( !flag){
fs.createReadStream(filePath).pipe(res); // 以前是可读流导入到可写流里
}else{
fs.createReadStream(filePath).pipe(flag).pipe(res);
}
}
sendError(error,req,res){
console.log(e);
res.statusCode = 404;
res.end('Not Found');
}
start(){
let server = http.createServer(this.handleRequest.bind(this));
server.listen(this.port, ()=>{
// 这一块的参数展现形式包括颜色 参考http-server 图1
console.log(`${chalk.yellow('Starting up http-server, serving')} ${chalk.blue('./')}
Available on:
http://127.0.0.1:${chalk.red('8080')}
http://192.168.0.100::${chalk.yellow('8080')}
Hit CTRL-C to stop the server`);
// 现在已经启动了这服务 现在需要能够访问 => handleRequest()
});
}
}
export default Server;

这里测试这个服务的时候 千万别忘了这个是es6语法 要在node中执行需要转化成es5 转化的服务要开起来 npm run babel:watch

Tips
__dirname 指当前执行文件的目录
process.cwd() 指当前执行命令的目录