目录
![]()
前言
静态资源服务器(HTTP 服务器)可以将静态文件(如 js、css、图片)等通过 HTTP 协议展现给客户端。本文介绍如何基于 Node 实现一个简易的静态资源服务器(类似于 serve)。
![]()
基础示例
- 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...");
- });
在上述示例中,我们创建了一个 http server,并以 stream 的方式返回一个 index.html 文件。其中,分别引入引入了 Node 中的 fs、fsp、http
模块,各个模块的作用如下:
![]()
简易 serve 实现
serve 的核心功能非常简单,它以命令行形式指定服务的文件根路径和服务端口,并创建一个 http 服务。
![]()
arg - 命令行参数读取
使用 npm 包 arg 读取命令行参数,www.npmjs.com/package/arg。
![]()
chalk - 控制台信息输出
使用 npm 包 chalk 在控制台输出带有颜色的文字信息,方便调试预览。
![]()
源码
- // 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;
-
- // 获取绝对路径
- let absolutePath = path.resolve(
- config["--entry"] || "",
- path.join(".", pathname)
- );
-
- let statusCode = 200;
- let fileStat = null;
-
- // 获取文件状态
- 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);
![]()
扩展
![]()
rewrite 和 redirect 的区别?
rewrite: 重写,是指服务器在接收到客户端的请求后,返回的是别的文件内容。举个例子,浏览器 A 向服务器请求了一个资源 indexA.html,服务器接收到 indexA.html 请求后,拿 indexB.html 作为实际的响应内容返回给浏览器 A,整个过程对于 A 来说是无感知的。
redirect: 重定向,浏览器 A 向服务器请求一个 index.html,服务器告诉浏览器 A “资源不在当前请求路径,需要去通过另外一个地址请求”。浏览器接收到新地址后,重新请求。整个过程中,浏览器需要请求两次。
到此这篇关于基于 Node 实现简易 serve(静态资源服务器)的文章就介绍到这了,更多相关node实现静态资源服务器内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!