基于 Node 实现简易 serve静态资源服务器的示例详解

 更新时间:2022年06月20日 15:39:50   作者:三天不喝水  
静态资源服务器(HTTP 服务器)可以将静态文件(如 js、css、图片)等通过 HTTP 协议展现给客户端。本文介绍如何基于 Node 实现一个简易的静态资源服务器,感兴趣的朋友一起看看吧

前言

静态资源服务器(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 模块,各个模块的作用如下:

  • fs: 文件系统模块,用于文件读写。
  • fsp: 使用 promise 形式的文件系统模块。
  • http: 用于搭建 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实现静态资源服务器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解nodejs通过代理(proxy)发送http请求(request)

    详解nodejs通过代理(proxy)发送http请求(request)

    本篇文章主要介绍了nodejs通过代理(proxy)发送http请求(request),具有一定的参考价值,有兴趣的可以了解一下
    2017-09-09
  • nodejs+socket.io实现p2p消息实时发送的项目实践

    nodejs+socket.io实现p2p消息实时发送的项目实践

    本文主要介绍了nodejs+socket.io实现p2p消息实时发送,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06
  • Node.js中的模块系统介绍

    Node.js中的模块系统介绍

    这篇文章介绍了Node.js中的模块系统,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04
  • 浅谈Node.js中的定时器

    浅谈Node.js中的定时器

    本文给大家分享的是Node.js中的定时器的相关资料,十分的全面细致,有需要的小伙伴可以参考下。
    2015-06-06
  • Node.js Koa2使用JWT进行鉴权的方法示例

    Node.js Koa2使用JWT进行鉴权的方法示例

    这篇文章主要介绍了Node.js Koa2使用JWT进行鉴权的方法示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • 利用Node.JS实现邮件发送功能

    利用Node.JS实现邮件发送功能

    其实利用Node.JS实现邮件发送这个功能很多人都写过了,但是网上有的代码不能用,版本较老,所以想着写下自己摸索的方法来实现。现在分享给大家,感兴趣的朋友们可以一起学习学习。
    2016-10-10
  • 前端Electron新手入门教程详解

    前端Electron新手入门教程详解

    这篇文章主要介绍了Electron新手入门教程详解,首先围绕Electron框架的关键知识点进行详细讲解,然后对DEMO程序进行分析,让前端开发人员对使用Electron开发桌面应用程序有一个初步的了解。,需要的朋友可以参考下
    2019-06-06
  • Node.js 与并发模型的详细介绍

    Node.js 与并发模型的详细介绍

    这篇文章主要介绍了Node.js 与并发模型的详细介绍,Node.js 现在已成为构建高并发网络应用服务工具箱中的一员,文章围绕主题展开详细的内容介绍,需要的朋友可以参考一下
    2022-07-07
  • 使用pm2运行node应用的流程步骤

    使用pm2运行node应用的流程步骤

    PM2是一个功能强大的进程管理工具,专为 Node.js 应用设计,它提供了进程管理、日志管理、负载均衡和性能监控等功能,帮助开发者轻松应对生产环境中的各种挑战,本文给大家介绍了使用pm2 运行node应用的流程步骤,需要的朋友可以参考下
    2024-03-03
  • Koa2微信公众号开发之消息管理

    Koa2微信公众号开发之消息管理

    这篇文章主要介绍了Koa2微信公众号开发之消息管理,这一节我们就来看看公众号的消息管理。并实现一个自动回复功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-05-05

最新评论