首先还是先感谢github,感谢github上提供此段源码的作者。跟昨晚的来比今天的静态文件服务器有点点复杂些,可以学到很多新的东西。
仔细会发现这次的代码多了一个fs.stat函数和ReadStream对象的pipe函数,stat这个函数是用来获取文件信息。第一个参数是传入文件路径,第二个则是回调函数,回调函数的第二个参数stats的属性为文件的基本信息。pipe函数用于将这个可读流和destination目标可写流连接起来,传入这个流中的数据将会写入到destination流中。通过在必要时暂停和恢复流,来源流和目的流得以保持同步。
该静态文件服务器的改进点在于使用了Last-Modified和If-Modified-Since报文头,可以不必要给浏览器返回它已经存在的文件。顺便可以根据浏览器请求资源的压缩方式返回给资源进行gzip或者deflate压缩。
rush:js;">
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(con<a href="https://www.jb51.cc/tag/fig/" target="_blank" class="keywords">fig</a>.Expires.fileMatch)) {
var expires = new Date();
expires.setTime(expires.getTime() + con<a href="https://www.jb51.cc/tag/fig/" target="_blank" class="keywords">fig</a>.Expires.maxAge * 1000);
response.setHeader("Expires",expires.toUTCString());
response.setHeader("Cache-Control","max-age=" + con<a href="https://www.jb51.cc/tag/fig/" target="_blank" class="keywords">fig</a>.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(con<a href="https://www.jb51.cc/tag/fig/" target="_blank" class="keywords">fig</a>.Compress.match);
if (matched && acceptEncoding.match(/\bgzip\b/)) {
response.writeHead(200,"Ok",{'<a href="https://www.jb51.cc/tag/contentencoding/" target="_blank" class="keywords">content-encoding</a>': 'gzip'});
raw.pipe(zlib.createGzip()).pipe(response);
} else if (matched && acceptEncoding.match(/\bdeflate\b/)) {
response.writeHead(200,{'<a href="https://www.jb51.cc/tag/contentencoding/" target="_blank" class="keywords">content-encoding</a>': '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年。
rush:js;">
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。
rush:js;">
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"
};