基于 Node 實(shí)現(xiàn)簡(jiǎn)易 serve靜態(tài)資源服務(wù)器的示例詳解
前言
靜態(tài)資源服務(wù)器(HTTP 服務(wù)器)可以將靜態(tài)文件(如 js、css、圖片)等通過(guò) HTTP 協(xié)議展現(xiàn)給客戶端。本文介紹如何基于 Node 實(shí)現(xiàn)一個(gè)簡(jiǎn)易的靜態(tài)資源服務(wù)器(類(lèi)似于 serve)。
基礎(chǔ)示例
const fs = require("node:fs");
const fsp = require("node:fs/promises");
const http = require("node:http");
const server = http.createServer(async (req, res) => {
const stat = await fsp.stat("./index.html");
res.setHeader("content-length", stat.size);
fs.createReadStream("./index.html").pipe(res);
});
server.listen(3000, () => {
console.log("Listening 3000...");
});在上述示例中,我們創(chuàng)建了一個(gè) http server,并以 stream 的方式返回一個(gè) index.html 文件。其中,分別引入引入了 Node 中的 fs、fsp、http 模塊,各個(gè)模塊的作用如下:
- fs: 文件系統(tǒng)模塊,用于文件讀寫(xiě)。
- fsp: 使用 promise 形式的文件系統(tǒng)模塊。
- http: 用于搭建 HTTP 服務(wù)器。
簡(jiǎn)易 serve 實(shí)現(xiàn)
serve 的核心功能非常簡(jiǎn)單,它以命令行形式指定服務(wù)的文件根路徑和服務(wù)端口,并創(chuàng)建一個(gè) http 服務(wù)。
arg - 命令行參數(shù)讀取
使用 npm 包 arg 讀取命令行參數(shù),www.npmjs.com/package/arg。
chalk - 控制臺(tái)信息輸出
使用 npm 包 chalk 在控制臺(tái)輸出帶有顏色的文字信息,方便調(diào)試預(yù)覽。
源碼
// Native - Node built-in module
const fs = require("node:fs");
const fsp = require("node:fs/promises");
const http = require("node:http");
const path = require("node:path");
// Packages
const arg = require("arg");
const chalk = require("chalk");
var config = {
entry: "",
rewrites: [],
redirects: [],
etag: false,
cleanUrls: false,
trailingSlash: false,
symlink: false,
};
/**
* eg: --port <string> or --port=<string>
* node advance.js -p 3000 | node advance.js --port 3000
*/
const args = arg({
"--port": Number,
"--entry": String,
"--rewrite": Boolean,
"--redirect": Boolean,
"--etag": Boolean,
"--cleanUrls": Boolean,
"--trailingSlash": Boolean,
"--symlink": Boolean,
});
const resourceNotFound = (response, absolutePath) => {
response.statusCode = 404;
fs.createReadStream(path.join("./html", "404.html")).pipe(response);
};
const processDirectory = async (absolutePath) => {
const newAbsolutePath = path.join(absolutePath, "index.html");
try {
const newStat = await fsp.lstat(newAbsolutePath);
return [newStat, newAbsolutePath];
} catch (e) {
return [null, newAbsolutePath];
}
};
/**
* @param { http.IncomingMessage } req
* @param { http.ServerResponse } res
* @param { config } config
*/
const handler = async (request, response, config) => {
// 從 request 中獲取 pathname
const pathname = new URL(request.url, `http://${request.headers.host}`)
.pathname;
// 獲取絕對(duì)路徑
let absolutePath = path.resolve(
config["--entry"] || "",
path.join(".", pathname)
);
let statusCode = 200;
let fileStat = null;
// 獲取文件狀態(tài)
try {
fileStat = await fsp.lstat(absolutePath);
} catch (e) {
// console.log(chalk.red(e));
}
if (fileStat?.isDirectory()) {
[fileStat, absolutePath] = await processDirectory(absolutePath);
}
if (fileStat === null) {
return resourceNotFound(response, absolutePath);
}
let headers = {
"Content-Length": fileStat.size,
};
response.writeHead(statusCode, headers);
fs.createReadStream(absolutePath).pipe(response);
};
const startEndpoint = (port, config) => {
const server = http.createServer((request, response) => {
// console.log("request: ", request);
handler(request, response, config);
});
server.on("error", (err) => {
const { code, port } = err;
if (code === "EADDRINUSE") {
console.log(chalk.red(`address already in use [:::${port}]`));
console.log(chalk.green(`Restart server on [:::${port + 1}]`));
startEndpoint(port + 1, entry);
return;
}
process.exit(1);
});
server.listen(port, () => {
console.log(chalk.green(`Open http://localhost:${port}`));
});
};
startEndpoint(args["--port"] || 3000, args);擴(kuò)展
rewrite 和 redirect 的區(qū)別?
- rewrite: 重寫(xiě),是指服務(wù)器在接收到客戶端的請(qǐng)求后,返回的是別的文件內(nèi)容。舉個(gè)例子,瀏覽器 A 向服務(wù)器請(qǐng)求了一個(gè)資源 indexA.html,服務(wù)器接收到 indexA.html 請(qǐng)求后,拿 indexB.html 作為實(shí)際的響應(yīng)內(nèi)容返回給瀏覽器 A,整個(gè)過(guò)程對(duì)于 A 來(lái)說(shuō)是無(wú)感知的。
- redirect: 重定向,瀏覽器 A 向服務(wù)器請(qǐng)求一個(gè) index.html,服務(wù)器告訴瀏覽器 A “資源不在當(dāng)前請(qǐng)求路徑,需要去通過(guò)另外一個(gè)地址請(qǐng)求”。瀏覽器接收到新地址后,重新請(qǐng)求。整個(gè)過(guò)程中,瀏覽器需要請(qǐng)求兩次。
到此這篇關(guān)于基于 Node 實(shí)現(xiàn)簡(jiǎn)易 serve(靜態(tài)資源服務(wù)器)的文章就介紹到這了,更多相關(guān)node實(shí)現(xiàn)靜態(tài)資源服務(wù)器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
node.js多個(gè)異步過(guò)程中判斷執(zhí)行是否完成的解決方案
這篇文章主要給大家介紹了關(guān)于node.js多個(gè)異步過(guò)程中判斷執(zhí)行是否完成的幾種解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-12-12
node.js中的dns.getServers方法使用說(shuō)明
這篇文章主要介紹了node.js中的dns.getServers方法使用說(shuō)明,本文介紹了dns.getServers方法說(shuō)明、語(yǔ)法和實(shí)現(xiàn)源碼,需要的朋友可以參考下2014-12-12
詳解nodejs微信公眾號(hào)開(kāi)發(fā)——3.封裝消息響應(yīng)模塊
上一篇文章:nodejs微信公眾號(hào)開(kāi)發(fā)(2)自動(dòng)回復(fù),實(shí)現(xiàn)了簡(jiǎn)單的關(guān)注回復(fù)。采用拼接字符串的形式,并不是很方便,這里我們將其封裝承接口。2017-04-04
node.js中的fs.readlink方法使用說(shuō)明
這篇文章主要介紹了node.js中的fs.readlink方法使用說(shuō)明,本文介紹了fs.readlink方法說(shuō)明、語(yǔ)法、接收參數(shù)、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下2014-12-12
Node.js發(fā)起HTTP請(qǐng)求的6種不同方法小結(jié)
本文主要介紹了Node.js發(fā)起HTTP請(qǐng)求的6種不同方法小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
配置node服務(wù)器并且鏈接微信公眾號(hào)接口配置步驟詳解
這篇文章主要介紹了配置node服務(wù)器并且鏈接微信公眾號(hào)接口配置步驟詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,,需要的朋友可以參考下2019-06-06
Node.js 使用request模塊下載文件的實(shí)例
今天小編就為大家分享一篇Node.js 使用request模塊下載文件的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09

