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 相关文章推荐
基于jquery的cookie的用法
Jan 10 Javascript
jquery 触发a链接点击事件解决方案
May 02 Javascript
点击弹出层外区域关闭弹出层jquery特效示例
Aug 25 Javascript
jQuery DOM操作实例
Mar 05 Javascript
jQuery菜单插件用法实例
Jul 25 Javascript
详解JavaScript正则表达式之分组匹配及反向引用
Mar 09 Javascript
zTree实现节点修改的实时刷新功能
Mar 20 Javascript
Node.js+Express+Mysql 实现增删改查
Apr 03 Javascript
微信小程序之下拉列表实现方法解析(附完整源码)
Aug 23 Javascript
原生JavaScript实现滑动拖动验证的示例代码
Dec 06 Javascript
javascript实现数字时钟效果
Feb 06 Javascript
vue-element-admin项目导入和导出的实现
May 21 Vue.js
实例讲解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常用函数小技巧
2008/09/11 PHP
PHP 巧用数组降低程序的时间复杂度
2010/01/01 PHP
php 模拟post_验证页面的返回状态(实例讲解)
2013/10/28 PHP
PHP QRCODE生成彩色二维码的方法
2016/05/19 PHP
JavaScript 语言的递归编程
2010/05/18 Javascript
基于jQuery的自动完成插件
2011/02/03 Javascript
nodejs win7下安装方法
2012/05/24 NodeJs
浅谈JS闭包中的循环绑定处理程序
2014/11/09 Javascript
Node.js中调用mysql存储过程示例
2014/12/20 Javascript
手机端 HTML5使用photoswipe.js仿微信朋友圈图片放大效果
2016/08/25 Javascript
JS获取浮动(float)元素的style.left值为空的快速解决办法
2017/02/19 Javascript
JS实现复选框的全选和批量删除功能
2017/04/05 Javascript
webpack4 处理CSS的方法示例
2018/09/03 Javascript
NodeJs实现简单的爬虫功能案例分析
2018/12/05 NodeJs
nodejs对项目下所有空文件夹创建gitkeep的方法
2019/08/02 NodeJs
Vue中跨域及打包部署到nginx跨域设置方法
2019/08/26 Javascript
vue props 单项数据流实例分享
2020/02/16 Javascript
为什么JavaScript中0.1 + 0.2 != 0.3
2020/12/03 Javascript
python SSH模块登录,远程机执行shell命令实例解析
2018/01/12 Python
删除python pandas.DataFrame 的多重index实例
2018/06/08 Python
python 判断三个数字中的最大值实例代码
2019/07/24 Python
Python Django框架url反向解析实现动态生成对应的url链接示例
2019/10/18 Python
浅谈keras.callbacks设置模型保存策略
2020/06/18 Python
Django多数据库联用实现方法解析
2020/11/12 Python
CSS3教程:background-clip和background-origin
2008/10/17 HTML / CSS
美国精品家居用品网站:US-Mattress
2016/08/24 全球购物
美国在线乐器和设备商店:Musician’s Friend
2018/07/06 全球购物
编写一子程序,将一链表倒序,即使链表表尾变表头,表头变表尾
2016/02/10 面试题
动态密码技术
2012/10/18 面试题
在浏览器端如何得到服务器端响应的XML数据
2012/11/24 面试题
水产养殖学应届生求职信
2013/09/29 职场文书
学校采购员岗位职责
2014/01/02 职场文书
小学运动会广播稿200字(十二篇)
2014/01/14 职场文书
2014年社区国庆节活动方案
2014/09/16 职场文书
幼儿园感恩节活动方案
2014/10/06 职场文书
稽核岗位职责范本
2015/04/13 职场文书