Node.js静态文件服务器改进版
首先还是先感谢github,感谢github上提供此段源码的作者。跟昨晚的来比今天的静态文件服务器有点点复杂些,可以学到很多新的东西。
仔细会发现这次的代码多了一个fs.stat函数和ReadStream对象的pipe函数,stat这个函数是用来获取文件信息。第一个参数是传入文件路径,第二个则是回调函数,回调函数的第二个参数stats的属性为文件的基本信息。pipe函数用于将这个可读流和destination目标可写流连接起来,传入这个流中的数据将会写入到destination流中。通过在必要时暂停和恢复流,来源流和目的流得以保持同步。
该静态文件服务器的改进点在于使用了Last-Modified和If-Modified-Since报文头,可以不必要给浏览器返回它已经存在的文件。顺便可以根据浏览器请求资源的压缩方式返回给资源进行gzip或者deflate压缩。
varPORT=8000;
varhttp=require("http");
varurl=require("url");
varfs=require("fs");
varpath=require("path");
varmime=require("./mime").types;
varconfig=require("./config");
varzlib=require("zlib");
varserver=http.createServer(function(request,response){
response.setHeader("Server","Node/V5");
varpathname=url.parse(request.url).pathname;
console.log("url="+pathname);
if(pathname.slice(-1)==="/"){
pathname=pathname+config.Welcome.file;
}
varrealPath=__dirname+"/"+path.join("assets",path.normalize(pathname.replace(/\.\./g,"")));
console.log("realPath="+realPath);
varpathHandle=function(realPath){
fs.stat(realPath,function(err,stats){
if(err){
response.writeHead(404,"NotFound",{'Content-Type':'text/plain'});
response.write("stats="+stats);
response.write("ThisrequestURL"+pathname+"wasnotfoundonthisserver.");
response.end();
}else{
if(stats.isDirectory()){
realPath=path.join(realPath,"/",config.Welcome.file);
pathHandle(realPath);
}else{
varext=path.extname(realPath);
ext=ext?ext.slice(1):'unknown';
varcontentType=mime[ext]||"text/plain";
response.setHeader("Content-Type",contentType);
//获得文件的修改时间
varlastModified=stats.mtime.toUTCString();
varifModifiedSince="If-Modified-Since".toLowerCase();
//设置Last-Modified
//服务器给浏览器返回文件最后一次修改时间Last-Modified
response.setHeader("Last-Modified",lastModified);
if(ext.match(config.Expires.fileMatch)){
varexpires=newDate();
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,"NotModified");
response.end();
}else{
varraw=fs.createReadStream(realPath);
varacceptEncoding=request.headers['accept-encoding']||"";
varmatched=ext.match(config.Compress.match);
if(matched&&acceptEncoding.match(/\bgzip\b/)){
response.writeHead(200,"Ok",{'Content-Encoding':'gzip'});
raw.pipe(zlib.createGzip()).pipe(response);
}elseif(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("Serverruningatport:"+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"
};