0

0

node静态文件服务器使用详解

php中世界最好的语言

php中世界最好的语言

发布时间:2018-03-29 09:18:21

|

1584人浏览过

|

来源于php中文网

原创

这次给大家带来node静态文件服务器使用详解,node静态文件服务器使用的注意事项有哪些,下面就是实战案例,一起来看一下。

本篇文章主要介绍了实战node静态文件服务器的示例,分享给大家,具体如下:

支持功能:

  1. 读取静态文件

  2. 访问目录可以自动寻找下面的index.html文件, 如果没有index.html则列出文件列表

  3. MIME类型支持

  4. 缓存支持/控制

  5. 支持gzip压缩

  6. Range支持,断点续传

  7. 全局命令执行

  8. 子进程运行

1. 创建服务读取静态文件

首先引入http模块,创建一个服务器,并监听配置端口:

 const http = require('http');
 
 const server = http.createServer();
 
 // 监听请求
 server.on('request', request.bind(this));
 
 server.listen(config.port, () => {
  console.log(`静态文件服务启动成功, 访问localhost:${config.port}`);
 });

写一个fn专门处理请求, 返回静态文件, url模块获取路径:

 const url = require('url');
 const fs = require('fs');
 function request(req, res) {
 const { pathname } = url.parse(req.url); // 访问路径
 
 const filepath = path.join(config.root, pathname); // 文件路径
 
 fs.createReadStream(filepath).pipe(res); // 读取文件,并响应
 }

支持寻找index.html:

 if (pathname === '/') {
  const rootPath = path.join(config.root, 'index.html');
  try{
   const indexStat = fs.statSync(rootPath);
   if (indexStat) {
    filepath = rootPath;
   }
  } catch(e) {
   
  }
 }

访问目录时,列出文件目录:

 fs.stat(filepath, (err, stats) => {
 if (err) {
  res.end('not found');
  return;
 }
 if (stats.isDirectory()) {
  let files = fs.readdirSync(filepath);
  files = files.map(file => ({
   name: file,
   url: path.join(pathname, file)
  }));
  let html = this.list()({
   title: pathname,
   files
  });
  res.setHeader('Content-Type', 'text/html');
  res.end(html);
 }
 }

html模板:

 function list() {
  let tmpl = fs.readFileSync(path.resolve(dirname, 'template', 'list.html'), 'utf8');
  return handlebars.compile(tmpl);
 }
 
 
 
 
 
 
 {{title}}
 
 
 

hope-server静态文件服务器

2.MIME类型支持

利用mime模块得到文件类型,并设置编码:

res.setHeader('Content-Type', mime.getType(filepath) + ';charset=utf-8');

3.缓存支持

http协议缓存:

Cache-Control: http1.1内容,告诉客户端如何缓存数据,以及规则

  1. private 客户端可以缓存

  2. public 客户端和代理服务器都可以缓存

  3. max-age=60 缓存内容将在60秒后失效

  4. no-cache 需要使用对比缓存验证数据,强制向源服务器再次验证

  5. no-store 所有内容都不会缓存,强制缓存和对比缓存都不会触发

Expires: http1.0内容,cache-control会覆盖,告诉客户端缓存什么时候过期

ETag: 内容的hash值 下一次客户端请求在请求头里添加if-none-match: etag值

Last-Modified: 最后的修改时间 下一次客户端请求在请求头里添加if-modified-since: Last-Modified值

 handleCache(req, res, stats, hash) {
 // 当资源过期时, 客户端发现上一次请求资源,服务器有发送Last-Modified, 则再次请求时带上if-modified-since
 const ifModifiedSince = req.headers['if-modified-since'];
 // 服务器发送了etag,客户端再次请求时用If-None-Match字段来询问是否过期
 const ifNoneMatch = req.headers['if-none-match'];
 // http1.1内容 max-age=30 为强行缓存30秒 30秒内再次请求则用缓存 private 仅客户端缓存,代理服务器不可缓存
 res.setHeader('Cache-Control', 'private,max-age=30');
 // http1.0内容 作用与Cache-Control一致 告诉客户端什么时间,资源过期 优先级低于Cache-Control
 res.setHeader('Expires', new Date(Date.now() + 30 * 1000).toGMTString());
 // 设置ETag 根据内容生成的hash
 res.setHeader('ETag', hash);
 // 设置Last-Modified 文件最后修改时间
 const lastModified = stats.ctime.toGMTString();
 res.setHeader('Last-Modified', lastModified);
 
 // 判断ETag是否过期
 if (ifNoneMatch && ifNoneMatch != hash) {
  return false;
 }
 // 判断文件最后修改时间
 if (ifModifiedSince && ifModifiedSince != lastModified) {
  return false;
 }
 // 如果存在且相等,走缓存304
 if (ifNoneMatch || ifModifiedSince) {
  res.writeHead(304);
  res.end();
  return true;
 } else {
  return false;
 }
 }

4.压缩

客户端发送内容,通过请求头里Accept-Encoding: gzip, deflate告诉服务器支持哪些压缩格式,服务器根据支持的压缩格式,压缩内容。如服务器不支持,则不压缩。

 getEncoding(req, res) {
  const acceptEncoding = req.headers['accept-encoding'];
  // gzip和deflate压缩
  if (/\bgzip\b/.test(acceptEncoding)) {
   res.setHeader('Content-Encoding', 'gzip');
   return zlib.createGzip();
  } else if (/\bdeflate\b/.test(acceptEncoding)) {
   res.setHeader('Content-Encoding', 'deflate');
   return zlib.createDeflate();
  } else {
   return null;
  }
 }

5.断点续传

服务器通过请求头中的Range: bytes=0-xxx来判断是否是做Range请求,如果这个值存在而且有效,则只发回请求的那部分文件内容,响应的状态码变成206,表示Partial Content,并设置Content-Range。如果无效,则返回416状态码,表明Request Range Not Satisfiable。如果不包含Range的请求头,则继续通过常规的方式响应。

 getStream(req, res, filepath, statObj) {
  let start = 0;
  let end = statObj.size - 1;
  const range = req.headers['range'];
  if (range) {
   res.setHeader('Accept-Range', 'bytes');
   res.statusCode = 206;//返回整个内容的一块
   let result = range.match(/bytes=(\d*)-(\d*)/);
   if (result) {
    start = isNaN(result[1]) ? start : parseInt(result[1]);
    end = isNaN(result[2]) ? end : parseInt(result[2]) - 1;
   }
  }
  return fs.createReadStream(filepath, {
   start, end
  });
 }

6.全局命令执行

通过npm link实现

  1. 为npm包目录创建软链接,将其链到{prefix}/lib/node_modules/

  2. 为可执行文件(bin)创建软链接,将其链到{prefix}/bin/{name}

npm link命令通过链接目录和可执行文件,实现npm包命令的全局可执行。

package.json里面配置

 {
 bin: {
 "hope-server": "bin/hope"
 }
 }

在项目下面创建bin目录 hope文件, 利用yargs配置命令行传参数

 // 告诉电脑用node运行我的文件
 #! /usr/bin/env node
 
 const yargs = require('yargs');
 const init = require('../src/index.js');
 const argv = yargs.option('d', {
 alias: 'root',
 demand: 'false',
 type: 'string',
 default: process.cwd(),
 description: '静态文件根目录'
 }).option('o', {
 alias: 'host',
 demand: 'false',
 default: 'localhost',
 type: 'string',
 description: '配置监听的主机'
 }).option('p', {
 alias: 'port',
 demand: 'false',
 type: 'number',
 default: 8080,
 description: '配置端口号'
 }).option('c', {
 alias: 'child',
 demand: 'false',
 type: 'boolean',
 default: false,
 description: '是否子进程运行'
 })
 .usage('hope-server [options]')
 .example(
 'hope-server -d / -p 9090 -o localhost', '在本机的9090端口上监听客户端的请求'
 ).help('h').argv;
 
 // 启动服务
 init(argv);

7.子进程运行

通过spawn实现

index.js

 const { spawn } = require('child_process');
 const Server = require('./hope');
 
 function init(argv) {
  // 如果配置为子进程开启服务
  if (argv.child) {
   //子进程启动服务
   const child = spawn('node', ['hope.js', JSON.stringify(argv)], {
    cwd: dirname,
    detached: true,
    stdio: 'inherit'
   });
 
   //后台运行
   child.unref();
   //退出主线程,让子线程单独运行
   process.exit(0);
  } else {
   const server = new Server(argv);
   server.start();
  }
 }
 
 module.exports = init;
hope.js
 if (process.argv[2] && process.argv[2].startsWith('{')) {
 const argv = JSON.parse(process.argv[2]);
 const server = new Hope(argv);
 server.start();
 }

8.源码及测试

源码地址: hope-server

npm install hope-server -g

进入任意目录

hope-server

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

use怎么注册Vue的全局组件

生活同城信息网系统
生活同城信息网系统

fankuan8生活同城信息网系统 v1206采用主流的Asp+Access开发设计,网站美工设计方面更大气,漂亮!网站浏览器兼容性也比较好,网站功能方面的细节方面十分强大。 网站程序的几大特点: 1.全站页面实行了伪静态化,各类型网站服务器的伪静态文件都已近处理好了,无需自己再做伪静态出来。 2.网站前台开始使用了fankuan8独立开发的互助链系统,开始使用时,在网站底部点击链接根据提示马上

下载

select的option叠加问题

相关专题

更多
Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

37

2026.01.14

php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

19

2026.01.13

PHP 高性能
PHP 高性能

本专题整合了PHP高性能相关教程大全,阅读专题下面的文章了解更多详细内容。

37

2026.01.13

MySQL数据库报错常见问题及解决方法大全
MySQL数据库报错常见问题及解决方法大全

本专题整合了MySQL数据库报错常见问题及解决方法,阅读专题下面的文章了解更多详细内容。

19

2026.01.13

PHP 文件上传
PHP 文件上传

本专题整合了PHP实现文件上传相关教程,阅读专题下面的文章了解更多详细内容。

16

2026.01.13

PHP缓存策略教程大全
PHP缓存策略教程大全

本专题整合了PHP缓存相关教程,阅读专题下面的文章了解更多详细内容。

6

2026.01.13

jQuery 正则表达式相关教程
jQuery 正则表达式相关教程

本专题整合了jQuery正则表达式相关教程大全,阅读专题下面的文章了解更多详细内容。

3

2026.01.13

交互式图表和动态图表教程汇总
交互式图表和动态图表教程汇总

本专题整合了交互式图表和动态图表的相关内容,阅读专题下面的文章了解更多详细内容。

45

2026.01.13

nginx配置文件详细教程
nginx配置文件详细教程

本专题整合了nginx配置文件相关教程详细汇总,阅读专题下面的文章了解更多详细内容。

9

2026.01.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
HTML5/CSS3/JavaScript/ES6入门课程
HTML5/CSS3/JavaScript/ES6入门课程

共102课时 | 6.7万人学习

前端基础到实战(HTML5+CSS3+ES6+NPM)
前端基础到实战(HTML5+CSS3+ES6+NPM)

共162课时 | 18.8万人学习

第二十二期_前端开发
第二十二期_前端开发

共119课时 | 12.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号