準(zhǔn)備工作
上一節(jié)實(shí)現(xiàn)了通過 commander 的配置獲取到用戶的參數(shù),下面完成借用 promise 寫成類的方法一節(jié)沒有完成的任務(wù),實(shí)現(xiàn)一個 http-server
,https://www.npmjs.com/package/http-server,http-server
是一個簡單的零配置命令行靜態(tài) HTTP 服務(wù)器。
需要用到的核心模塊
const http = require("http");
const path = require("path");
const url = require("url");
const fs = require("fs").promises;
const { createReadStream, createWriteStream, readFileSync } = require("fs");
需要用到的第三方模塊:
- ejs 用來進(jìn)行模板渲染
- mime 用來根據(jù)文件擴(kuò)展名獲取 MIME type, 也可以根據(jù) MIME type 獲取擴(kuò)展名。
- chalk 用來控制臺輸出著色
- debug 可以根據(jù)環(huán)境變量來進(jìn)行打印
const ejs = require("ejs"); // 服務(wù)端讀取目錄進(jìn)行渲染
const mime = require("mime");
const chalk = require("chalk");
const debug = require("debug")("server");
安裝依賴
npm install ejs@3.1.3 debug@4.1.1 mime@2.4.6 chalk@4.1.0
配置環(huán)境變量
const debug = require("debug")("server");
// 根據(jù)環(huán)境變量來進(jìn)行打印 process.env.EDBUG
debug("hello kaimo-http-server");
set DEBUG=server
kaimo-http-server
代碼實(shí)現(xiàn)
- 將拿到的配置數(shù)據(jù)開啟一個 server
在 www.js
里面實(shí)現(xiàn)
#! /usr/bin/env node
const program = require("commander");
const { version } = require("../package.json");
const config = require("./config.js");
const Server = require("../src/server.js");
program.name("kaimo-http-server");
program.usage("[args]");
program.version(version);
Object.values(config).forEach((val) => {
if (val.option) {
program.option(val.option, val.description);
}
});
program.on("--help", () => {
console.log("\r\nExamples:");
Object.values(config).forEach((val) => {
if (val.usage) {
console.log(" " + val.usage);
}
});
});
// 解析用戶的參數(shù)
let parseObj = program.parse(process.argv);
let keys = Object.keys(config);
// 最終用戶拿到的數(shù)據(jù)
let resultConfig = {};
keys.forEach((key) => {
resultConfig[key] = parseObj[key] || config[key].default;
});
// 將拿到的配置數(shù)據(jù)開啟一個 server
console.log("resultConfig---->", resultConfig);
let server = new Server(resultConfig);
server.start();
- 實(shí)現(xiàn)
server.js
,主要的邏輯就是通過核心模塊以及第三方模塊將用戶輸入的參數(shù),實(shí)現(xiàn)一個 Server 類,里面通過 start 方法開啟服務(wù),核心邏輯實(shí)現(xiàn)通過路徑找到這個文件返回,如果是文件夾處理是否有index.html
文件,沒有的話就通過模板渲染文件夾里目錄信息。
// 核心模塊
const http = require("http");
const path = require("path");
const url = require("url");
const fs = require("fs").promises;
const { createReadStream, createWriteStream, readFileSync } = require("fs");
// 第三方模塊
const ejs = require("ejs"); // 服務(wù)端讀取目錄進(jìn)行渲染
const mime = require("mime");
const chalk = require("chalk");
const debug = require("debug")("server");
// 根據(jù)環(huán)境變量來進(jìn)行打印 process.env.EDBUG
debug("hello kaimo-http-server");
// 同步讀取模板
const template = readFileSync(path.resolve(__dirname, "template.ejs"), "utf-8");
class Server {
constructor(config) {
this.host = config.host;
this.port = config.port;
this.directory = config.directory;
this.template = template;
}
async handleRequest(req, res) {
let { pathname } = url.parse(req.url);
// 需要對 pathname 進(jìn)行一次轉(zhuǎn)義,避免訪問中文名稱文件找不到問題
console.log(pathname);
pathname = decodeURIComponent(pathname);
console.log(pathname);
// 通過路徑找到這個文件返回
let filePath = path.join(this.directory, pathname);
console.log(filePath);
try {
// 用流讀取文件
let statObj = await fs.stat(filePath);
// 判斷是否是文件
if (statObj.isFile()) {
this.sendFile(req, res, filePath, statObj);
} else {
// 文件夾的話就先嘗試找找 index.html
let concatFilePath = path.join(filePath, "index.html");
try {
let statObj = await fs.stat(concatFilePath);
this.sendFile(req, res, concatFilePath, statObj);
} catch (e) {
// index.html 不存在就列出目錄
this.showList(req, res, filePath, statObj, pathname);
}
}
} catch (e) {
this.sendError(req, res, e);
}
}
// 列出目錄
async showList(req, res, filePath, statObj, pathname) {
// 讀取目錄包含的信息
let dirs = await fs.readdir(filePath);
console.log(dirs, "-------------dirs----------");
try {
let parseObj = dirs.map((item) => ({
dir: item,
href: path.join(pathname, item) // url路徑拼接自己的路徑
}));
// 渲染列表:這里采用異步渲染
let templateStr = await ejs.render(this.template, { dirs: parseObj }, { async: true });
console.log(templateStr, "-------------templateStr----------");
res.setHeader("Content-type", "text/html;charset=utf-8");
res.end(templateStr);
} catch (e) {
this.sendError(req, res, e);
}
}
// 讀取文件返回
sendFile(req, res, filePath, statObj) {
// 設(shè)置類型
res.setHeader("Content-type", mime.getType(filePath) + ";charset=utf-8");
// 讀取文件進(jìn)行響應(yīng)
createReadStream(filePath).pipe(res);
}
// 專門處理錯誤信息
sendError(req, res, e) {
debug(e);
res.statusCode = 404;
res.end("Not Found");
}
start() {
const server = http.createServer(this.handleRequest.bind(this));
server.listen(this.port, this.host, () => {
console.log(chalk.yellow(`Starting up kaimo-http-server, serving ./${this.directory.split("\\").pop()}\r\n`));
console.log(chalk.green(` http://${this.host}:${this.port}`));
});
}
}
module.exports = Server;
- 模板
template.ejs
的代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>列表模板</title>
</head>
<body>
<% dirs.forEach(item=> { %>
<li>
<a href="<%=item.href%>"><%=item.dir%></a>
</li>
<% }) %>
</body>
</html>
實(shí)現(xiàn)效果
控制臺我們輸入下面命令啟動一個端口為 4000 的服務(wù)
kaimo-http-server --port 4000
我們訪問 http://localhost:4000/
,可以看到
我們在進(jìn)入 public 文件,里面有 index.html
文件
點(diǎn)擊 public 目錄進(jìn)入 http://localhost:4000/public
,可以看到返回了 index.html
頁面
我們在進(jìn)入 public2 文件,里面沒有 index.html
文件
點(diǎn)擊 public2 目錄進(jìn)入 http://localhost:4000/public2
,看到的就是列表
我們可以點(diǎn)擊任何的文件查看:比如 凱小默.txt
文章來源:http://www.zghlxwxcb.cn/news/detail-635700.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-635700.html
到了這里,關(guān)于64 # 實(shí)現(xiàn)一個 http-server的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!