经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » Node.js » 查看文章
基于 Node 实现简易 serve静态资源服务器的示例详解
来源:jb51  时间:2022/6/20 17:07:45  对本文有异议
目录

前言

静态资源服务器(HTTP 服务器)可以将静态文件(如 js、css、图片)等通过 HTTP 协议展现给客户端。本文介绍如何基于 Node 实现一个简易的静态资源服务器(类似于 serve)。

基础示例

  1. const fs = require("node:fs");
  2. const fsp = require("node:fs/promises");
  3. const http = require("node:http");
  4.  
  5. const server = http.createServer(async (req, res) => {
  6.   const stat = await fsp.stat("./index.html");
  7.   res.setHeader("content-length", stat.size);
  8.   fs.createReadStream("./index.html").pipe(res);
  9. });
  10.  
  11. server.listen(3000, () => {
  12.   console.log("Listening 3000...");
  13. });

在上述示例中,我们创建了一个 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 在控制台输出带有颜色的文字信息,方便调试预览。

源码

  1. // Native - Node built-in module
  2. const fs = require("node:fs");
  3. const fsp = require("node:fs/promises");
  4. const http = require("node:http");
  5. const path = require("node:path");
  6.  
  7. // Packages
  8. const arg = require("arg");
  9. const chalk = require("chalk");
  10.  
  11. var config = {
  12.   entry: "",
  13.   rewrites: [],
  14.   redirects: [],
  15.   etag: false,
  16.   cleanUrls: false,
  17.   trailingSlash: false,
  18.   symlink: false,
  19. };
  20.  
  21. /**
  22.  * eg: --port <string> or --port=<string>
  23.  * node advance.js -p 3000 | node advance.js --port 3000
  24.  */
  25. const args = arg({
  26.   "--port": Number,
  27.   "--entry": String,
  28.   "--rewrite": Boolean,
  29.   "--redirect": Boolean,
  30.   "--etag": Boolean,
  31.   "--cleanUrls": Boolean,
  32.   "--trailingSlash": Boolean,
  33.   "--symlink": Boolean,
  34. });
  35.  
  36. const resourceNotFound = (response, absolutePath) => {
  37.   response.statusCode = 404;
  38.   fs.createReadStream(path.join("./html", "404.html")).pipe(response);
  39. };
  40.  
  41. const processDirectory = async (absolutePath) => {
  42.   const newAbsolutePath = path.join(absolutePath, "index.html");
  43.  
  44.   try {
  45.     const newStat = await fsp.lstat(newAbsolutePath);
  46.     return [newStat, newAbsolutePath];
  47.   } catch (e) {
  48.     return [null, newAbsolutePath];
  49.   }
  50. };
  51.  
  52. /**
  53.  * @param { http.IncomingMessage } req
  54.  * @param { http.ServerResponse } res
  55.  * @param { config } config
  56.  */
  57. const handler = async (request, response, config) => {
  58.   // 从 request 中获取 pathname
  59.   const pathname = new URL(request.url, `http://${request.headers.host}`)
  60.     .pathname;
  61.  
  62.   // 获取绝对路径
  63.   let absolutePath = path.resolve(
  64.     config["--entry"] || "",
  65.     path.join(".", pathname)
  66.   );
  67.  
  68.   let statusCode = 200;
  69.   let fileStat = null;
  70.  
  71.   // 获取文件状态
  72.   try {
  73.     fileStat = await fsp.lstat(absolutePath);
  74.   } catch (e) {
  75.     // console.log(chalk.red(e));
  76.   }
  77.  
  78.   if (fileStat?.isDirectory()) {
  79.     [fileStat, absolutePath] = await processDirectory(absolutePath);
  80.   }
  81.  
  82.   if (fileStat === null) {
  83.     return resourceNotFound(response, absolutePath);
  84.   }
  85.  
  86.   let headers = {
  87.     "Content-Length": fileStat.size,
  88.   };
  89.  
  90.   response.writeHead(statusCode, headers);
  91.  
  92.   fs.createReadStream(absolutePath).pipe(response);
  93. };
  94.  
  95. const startEndpoint = (port, config) => {
  96.   const server = http.createServer((request, response) => {
  97.     // console.log("request: ", request);
  98.     handler(request, response, config);
  99.   });
  100.  
  101.   server.on("error", (err) => {
  102.     const { code, port } = err;
  103.     if (code === "EADDRINUSE") {
  104.       console.log(chalk.red(`address already in use [:::${port}]`));
  105.       console.log(chalk.green(`Restart server on [:::${port + 1}]`));
  106.       startEndpoint(port + 1, entry);
  107.       return;
  108.     }
  109.     process.exit(1);
  110.   });
  111.  
  112.   server.listen(port, () => {
  113.     console.log(chalk.green(`Open http://localhost:${port}`));
  114.   });
  115. };
  116.  
  117. startEndpoint(args["--port"] || 3000, args);

扩展

rewrite 和 redirect 的区别?

  • rewrite: 重写,是指服务器在接收到客户端的请求后,返回的是别的文件内容。举个例子,浏览器 A 向服务器请求了一个资源 indexA.html,服务器接收到 indexA.html 请求后,拿 indexB.html 作为实际的响应内容返回给浏览器 A,整个过程对于 A 来说是无感知的。

  • redirect: 重定向,浏览器 A 向服务器请求一个 index.html,服务器告诉浏览器 A &ldquo;资源不在当前请求路径,需要去通过另外一个地址请求&rdquo;。浏览器接收到新地址后,重新请求。整个过程中,浏览器需要请求两次。

到此这篇关于基于 Node 实现简易 serve(静态资源服务器)的文章就介绍到这了,更多相关node实现静态资源服务器内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号