依赖http模块,服务器
使用http模块的createServer方法,创建一个服务器
const http = require("http")
let server = http.createServer((req,res)=>{
res.status = 200
res.setHeader("Content-Type", "text/plain")
res.end("hello node server")
})
server.listen(8000, "localhost", ()=>{
console.log("server running on 8000")
})
静态http服务器读取文件和文件夹
使用fs模块,首先判断文件是不是文件夹,如果不是文件夹,则使用fs进行读取文件内容。
fs.stat:判断文件的状态
fs.readdir:读取文件夹
const http = require("http")
const fs = require('fs')
let server = http.createServer((req,res)=>{
fs.stat(filePath, (_err, stats) => {
try {
if (stats.isFile()) {
res.statusCode = 200;
res.setHeader("Content-Type", "text/plain; charset=utf-8");
// 将读取的文件内容输出到页面
fs.createReadStream(filePath).pipe(res);
} else if (stats.isDirectory()) {
// 如果是文件夹,将内部的文件名读取出来,用逗号拼接成字符串
fs.readdir(filePath, (err, files) => {
res.statusCode = 200;
res.setHeader("Content-Type", "text/plain; charset=utf-8");
res.end(files.join(","));
return;
});
}
}catch (error) {
res.statusCode = 404;
res.setHeader("Content-Type", "text/plain;charset=utf-8");
res.end("访问的路径不存在");
}
});
res.status = 200
res.setHeader("Content-Type", "text/plain")
res.end("hello node server")
})
server.listen(8000, "localhost", ()=>{
console.log("server running on 8000")
})
使用handlerbars模板,显示文件列表
安装npm install —save handlerbars;
handlerbars的使用,使用compiler方法
<script src="https://cdn.jsdelivr.net/npm/handlebars@latest/dist/handlebars.js"></script>
<script>
// compile the template
var template = Handlebars.compile("Handlebars <b>{{doesWhat}}</b>");
// execute the compiled template and print the output to the console
console.log(template({ doesWhat: "rocks!" }));
</script>
新建dir.tpl模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{title}}</title>
<style>
a{
display:block;
font-size:30px;
}
</style>
</head>
<body>
{{#each files}}
<a href="{{../dir}}/{{this}}">{{this}}</a>
{{/each}}
</body>
</html>
route.js
// 获取tpl模板内容
const tplPath = path.join(__dirname, "./template/dir.tpl");
// 将获取的模板内容,转为字符串
const source = await fs.readFileSync(tplPath);
const template = Handlebars.compile(`${source.toString()}`);
const dir = path.relative(process.cwd(), filePath);
// 将data数据返回给handlerbars模板使用
const data = {
files: files,
title: path.basename(filePath),
dir: dir ? `/${dir}` : "",
// dir: path.join(__dirname),
};
res.end(template(data));
Content-Type设置文件类型
res.setHeader("Content-Type", `text/plain; charset=utf-8`);
使用mime.js对不同后缀文件进行的动态匹配。
可以安装npm install mine —save
const fileType = mine.getType(path.extname(filePath));
res.setHeader("Content-Type", `${fileType}; charset=utf-8`);
accept/content-encoding文件压缩
Request中使用accept-encoding表示能接收的压缩类型
response中使用content-encoding表示服务器把文件压缩的方式
新建compress.js压缩文件的函数
const { createGzip, createDeflate } = require("zlib");
module.exports = (rs, req, res) => {
// 获取req请求头的accept-encoding字段
const acceptEncoding = req.headers["accept-encoding"];
// 如果值不存在或不是gzip和deflate的压缩格式,不进行处理
if (!acceptEncoding || !acceptEncoding.match(/\b(gzip|deflate)\b/)) {
return rs;
} else if (acceptEncoding.match(/\bgzip\b/)) {
// 服务器端设置响应header的Content-Encoding为gzip
res.setHeader("Content-Encoding", "gzip");
return rs.pipe(createGzip());
} else if (acceptEncoding.match(/\deflate\b/)) {
// 服务器端设置header的Content-Encoding为deflate
res.setHeader("Content-Encoding", "deflate");
return rs.pipe(createDeflate());
}
};
使用compress方法
let result = fs.createReadStream(filePath);
// 匹配到可以被压缩的文件时,对文件进行压缩
if (filePath.match(config.compress)) {
result = compress(result, req, res);
}
result.pipe(res);
range头,获取部分内容
有时候为了获取部分内容,可使用range头进行设置
设置的range头返回的内容,状态码为206
新建range.js方法
module.exports = (totalSize, req, res) => {
const range = req.headers["range"];
if (!range) {
return { code: 200 };
}
const sizes = range.match(/bytes=(\d*)-(\d*)/);
const end = sizes[2] || totalSize - 1;
const start = sizes[1] || totalSize - end;
if (start > end || start < 0 || end > totalSize) {
return { code: 200 };
}
// 服务器端设置响应头:Accept-Ranges、Content-Range、Content-Length
res.setHeader("Accept-Ranges", "bytes");
res.setHeader("Content-Range", `bytes ${start} - ${end}/${totalSize}`);
res.setHeader("Content-Length", end - start);
return {
code: 206,
start: parseInt(start),
end: parseInt(end),
};
};
使用range方法
let result;
const { code, start, end } = range(stats.size, req, res);
if (code == 200) {
res.statusCode = 200;
result = fs.createReadStream(filePath);
} else {
res.statusCode = 206;
result = fs.createReadStream(filePath, { start, end });
}
缓存cache的请求头和响应头
- Expires(返回的绝对时间,返回截止时间,由于时区原因误差大,比较少用。出现的比较早期。),Cache-Control (返回的相对时间,往后推迟多长时间,用的比较多)
- If-Modified-Since(客户端请求header的修改时间) / Last-Modified(服务器端响应返回的修改时间)
- If-None-Match(客户端请求) / ETag(服务器端响应)(只要文件一改变,就发生状态改变)
创建cache.js文件
const { cache } = require("../config");
function refreshRes(stats, res) {
const { maxAge, expires, cacheControl, lastModified, etag } = cache;
if (expires) {
res.setHeader("Expires", new Date(Date.now() + maxAge).toUTCString());
}
if (cacheControl) {
res.setHeader("Cache-Control", `public, max-age=${maxAge}`);
}
if (lastModified) {
res.setHeader("Last-Modified", stats.mtime.toUTCString());
}
if (etag) {
res.setHeader("ETag", `${stats.size}-${stats.mtime}`);
}
}
module.exports = function (stats, req, res) {
refreshRes(stats, res);
const lastModified = req.headers["if-modified-since"];
const etag = req.headers["if-none-match"];
if (!lastModified && !etag) {
return false;
}
if (lastModified && lastModified !== res.getHeader("Last-Modified")) {
return false;
}
if (etag && etag !== res.getHeader("ETag")) {
return false;
}
return true;
};
使用cache缓存
// 判断缓存
if (cache(stats, req, res)) {
// 命中缓存,状态码304
res.statusCode = 304;
res.end();
return;
}
将静态服务器发布为cli工具
使用yargs获取命令行参数
安装yargs,npm i yargs —save
创建app.js
const yargs = require("yargs");
const argv = yargs
.option("p", {
alias: "port",
describe: "端口号",
default: 3666,
})
.option("h", {
alias: "hostname",
describe: "host",
default: "127.0.0.1",
})
.option("d", {
alias: "root",
describe: "root path",
default: process.cwd(),
})
.version()
.alias("v", "version")
.help().argv;
// 然后可以将argv参数进行保存传递
console.log(argv)
创建bin文件
创建bin文件夹,在文件下创建anyopen文件
#! /usr/bin/env node
require("../index.js")
第一句表示用node命令执行,执行index.js文件。
发布到npm
- npm login: 登录
- npm publish: 发布
本地安装和使用
全局安装
npm i -g anyopenserver
然后就可以在本地的文件夹中启动一个服务器
运行:anyopen