Node.js静态文件服务器改进版


Posted in Javascript onJanuary 10, 2016

首先还是先感谢github,感谢github上提供此段源码的作者。跟昨晚的来比今天的静态文件服务器有点点复杂些,可以学到很多新的东西。

仔细会发现这次的代码多了一个fs.stat函数和ReadStream对象的pipe函数,stat这个函数是用来获取文件信息。第一个参数是传入文件路径,第二个则是回调函数,回调函数的第二个参数stats的属性为文件的基本信息。pipe函数用于将这个可读流和destination目标可写流连接起来,传入这个流中的数据将会写入到destination流中。通过在必要时暂停和恢复流,来源流和目的流得以保持同步。

该静态文件服务器的改进点在于使用了Last-Modified和If-Modified-Since报文头,可以不必要给浏览器返回它已经存在的文件。顺便可以根据浏览器请求资源的压缩方式返回给资源进行gzip或者deflate压缩。

var PORT = 8000;
var http = require("http");
var url = require("url");
var fs = require("fs");
var path = require("path");
var mime = require("./mime").types;
var config = require("./config");
var zlib = require("zlib");
var server = http.createServer(function(request, response) {
 response.setHeader("Server", "Node/V5");
 var pathname = url.parse(request.url).pathname;
 console.log("url = " + pathname);
 if (pathname.slice(-1) === "/") {
  pathname = pathname + config.Welcome.file;
 }
 var realPath = __dirname + "/" + path.join("assets", path.normalize(pathname.replace(/\.\./g, "")));
 console.log("realPath = " + realPath);
 var pathHandle = function (realPath) {
  fs.stat(realPath, function (err, stats) {
   if (err) {
    response.writeHead(404, "Not Found", {'Content-Type': 'text/plain'});
    response.write("stats = " + stats);
    response.write("This request URL " + pathname + " was not found on this server.");
    response.end();
   } else {
    if (stats.isDirectory()) {
     realPath = path.join(realPath, "/", config.Welcome.file);
     pathHandle(realPath);
    } else {
     var ext = path.extname(realPath);
     ext = ext ? ext.slice(1) : 'unknown';
     var contentType = mime[ext] || "text/plain";
     response.setHeader("Content-Type", contentType);
     //获得文件的修改时间 
     var lastModified = stats.mtime.toUTCString();
     var ifModifiedSince = "If-Modified-Since".toLowerCase();
     //设置Last-Modified
     //服务器给浏览器返回文件最后一次修改时间Last-Modified
     response.setHeader("Last-Modified", lastModified);

     if (ext.match(config.Expires.fileMatch)) {
      var expires = new Date();
      expires.setTime(expires.getTime() + config.Expires.maxAge * 1000);
      response.setHeader("Expires", expires.toUTCString());
      response.setHeader("Cache-Control", "max-age=" + config.Expires.maxAge);
     }
     //服务器接收浏览器发送过来的If-Modified-Since报文头
     //日期相同表示该资源没有变化则返回304
     //告诉浏览器该资源你已经有了,不需要再请求了
     if (request.headers[ifModifiedSince] && lastModified == request.headers[ifModifiedSince]) {
      response.writeHead(304, "Not Modified");
      response.end();
     } else {
      var raw = fs.createReadStream(realPath);
      var acceptEncoding = request.headers['accept-encoding'] || "";
      var matched = ext.match(config.Compress.match); 
      if (matched && acceptEncoding.match(/\bgzip\b/)) {
       response.writeHead(200, "Ok", {'Content-Encoding': 'gzip'});
       raw.pipe(zlib.createGzip()).pipe(response);
      } else if (matched && acceptEncoding.match(/\bdeflate\b/)) {
       response.writeHead(200, "Ok", {'Content-Encoding': 'deflate'});
       raw.pipe(zlib.createDeflate()).pipe(response);
      } else {
       response.writeHead(200, "Ok");
       raw.pipe(response);
      }
     }
    }
   }
  });
 };

 pathHandle(realPath);
});
server.listen(PORT);
console.log("Server runing at port: " + PORT + ".");

Expires字段声明了一个网页或URL地址不再被浏览器缓存的时间,一旦超过了这个时间,浏览器都应该联系原始服务器。这里设置失效时间为1年。

exports.Expires = {
 fileMatch: /^(gif|png|jpg|js|css)$/ig,
 maxAge: 60*60*24*365
};
exports.Compress = {
 match: /css|js|html/ig
};
exports.Welcome = {
 file: "index.html"
};

枚举各种资源的类型,可根据扩展名设置Content-Type。

exports.types = {
 "css": "text/css",
 "gif": "image/gif",
 "html": "text/html",
 "ico": "image/x-icon",
 "jpeg": "image/jpeg",
 "jpg": "image/jpeg",
 "js": "text/javascript",
 "json": "application/json",
 "pdf": "application/pdf",
 "png": "image/png",
 "svg": "image/svg+xml",
 "swf": "application/x-shockwave-flash",
 "tiff": "image/tiff",
 "txt": "text/plain",
 "wav": "audio/x-wav",
 "wma": "audio/x-ms-wma",
 "wmv": "video/x-ms-wmv",
 "xml": "text/xml"
};
Javascript 相关文章推荐
克隆javascript对象的三个方法小结
Jan 12 Javascript
SOSO地图API使用(一)在地图上画圆实现思路与代码
Jan 15 Javascript
Dojo Javascript 编程规范 规范自己的JavaScript书写
Oct 26 Javascript
jQuery实现悬浮在右上角的网页客服效果代码
Oct 24 Javascript
多个js毫秒倒计时同时进行效果
Jan 05 Javascript
AngularJS中的Directive实现延迟加载
Jan 25 Javascript
Vue render深入开发讲解
Apr 13 Javascript
微信小程序上传文件到阿里OSS教程
May 20 Javascript
Vue CL3 配置路径别名详解
May 30 Javascript
javascript面向对象三大特征之多态实例详解
Jul 24 Javascript
element表格翻页第2页从1开始编号(后端从0开始分页)
Dec 10 Javascript
用云开发Cloudbase实现小程序多图片内容安全监测的代码详解
Jun 07 Javascript
实例讲解javascript注册事件处理函数
Jan 09 #Javascript
详解javascript事件冒泡
Jan 09 #Javascript
js父页面中使用子页面的方法
Jan 09 #Javascript
jquery调整表格行tr上下顺序实例讲解
Jan 09 #Javascript
实例讲解js验证表单项是否为空的方法
Jan 09 #Javascript
小心!AngularJS结合RequireJS做文件合并压缩的那些坑
Jan 09 #Javascript
javascript跑马灯抽奖实例讲解
Apr 17 #Javascript
You might like
php 计划任务 检测用户连接状态
2012/03/29 PHP
PHP APC的安装与使用详解
2013/06/13 PHP
php简单截取字符串代码示例
2016/10/19 PHP
PHP基于swoole多进程操作示例
2019/08/12 PHP
面向对象的Javascript之一(初识Javascript)
2012/01/20 Javascript
JavaScript高级程序设计(第3版)学习笔记2 js基础语法
2012/10/11 Javascript
node.js中的fs.unlinkSync方法使用说明
2014/12/15 Javascript
jquery文档操作wrap()方法实例简述
2015/01/10 Javascript
jQuery使用fadein方法实现渐出效果实例
2015/03/27 Javascript
jquery实现适用于门户站的导航下拉菜单效果代码
2015/08/24 Javascript
js时间戳和c#时间戳互转方法(推荐)
2017/02/15 Javascript
canvas简单快速的实现知乎登录页背景效果
2017/05/08 Javascript
vuejs 单文件组件.vue 文件的使用
2017/07/28 Javascript
利用百度地图API获取当前位置信息的实例
2017/11/06 Javascript
javascript自定义事件功能与用法实例分析
2017/11/08 Javascript
详解jQuery设置内容和属性
2019/04/11 jQuery
如何检查一个对象是否为空
2019/04/11 Javascript
JS 数组基本用法入门示例解析
2020/01/16 Javascript
[17:00]DOTA2 HEROS教学视频教你分分钟做大人-帕克
2014/06/10 DOTA
[01:02:48]2018DOTA2亚洲邀请赛 4.1 小组赛 A组 LGD vs OG
2018/04/02 DOTA
python入门前的第一课 python怎样入门
2018/03/06 Python
matplotlib给子图添加图例的方法
2018/08/03 Python
NumPy 数组使用大全
2019/04/25 Python
PyQt5 closeEvent关闭事件退出提示框原理解析
2020/01/08 Python
keras自定义损失函数并且模型加载的写法介绍
2020/06/15 Python
Python Pivot table透视表使用方法解析
2020/09/11 Python
CSS3实现自定义Checkbox特效实例代码
2017/04/24 HTML / CSS
英国最大的百货公司:Harrods
2016/08/18 全球购物
海外淘书首选:AbeBooks
2017/07/31 全球购物
英国时尚优质的女装:Hope Fashion
2018/08/14 全球购物
帕克纽约:PARKER NY
2018/12/09 全球购物
李维斯牛仔裤英国官方网站:Levi’s英国
2019/10/10 全球购物
采购员的工作职责
2013/12/26 职场文书
大学生饮食连锁店创业计划书
2014/01/17 职场文书
农村党员学习党的群众路线教育实践活动心得体会
2014/11/04 职场文书
navicat 连接Ubuntu虚拟机的mysql的操作方法
2022/04/02 MySQL