NodeJS 使用 http 实现静态文件服务器。
1.1.1. app.js
var PORT = 8000;
var http = require("http");
var url = require("url");
var path = require("path");
var fs = require("fs");
var util = require("util");
var mime = require("./mime").types;
var configExpires = require("./config").Expires;
var configCompress = require("./config").Compress;
var zlib = require('zlib');
var server = http.createServer(function (req, resp) {
var reqURI = url.parse(req.url);
var pathname = reqURI.pathname;
if(pathname == '/favicon.ico') {
return resp.end("/favicon.icon");
}
var realpath = path.join(__dirname + "/assets", pathname);
var ext = path.extname(realpath);
ext = ext ? ext.slice(1) : 'unknown';
if(ext.match(configExpires.fileMatch)){
var expires = new Date();
expires.setTime(expires.getTime() + configExpires.maxAge * 1000);
resp.setHeader('Expires', expires.toUTCString());
resp.setHeader('Cache-Control', 'max-age=' + configExpires.maxAge);
}
var contentType = mime[ext] || 'text/plain';
if(contentType.indexOf('text')>=0){
contentType += "; charset=utf-8";
}
var acceptEncoding = req.headers['accept-encoding'];
console.log(acceptEncoding);
fs.stat(realpath, function cb (err, stat) {
if(err){
resp.writeHead(404, {
'Content-Type': contentType,
});
return resp.end("文件不存在!");
}
var lastModified = stat.mtime.toUTCString();
resp.setHeader('Last-Modified', lastModified);
if(req.headers['if-modified-since'] && lastModified == req.headers['if-modified-since']){
resp.writeHead(304, 'Not Modified');
return resp.end();
}
var raw = fs.createReadStream(realpath);
var matchCompress = ext.match(configCompress.match);
if(matchCompress && acceptEncoding.match(/\bgzip\b/)){
console.log("compress:" + contentType);
resp.writeHead(200, {
'Content-Type': contentType,
'Content-Encoding': 'gzip',
});
return raw.pipe(zlib.createGzip()).pipe(resp);
}
if(matchCompress && acceptEncoding.match(/\deflate\b/)){
resp.writeHead(200, {
'Content-Type': contentType,
'Content-Encoding': 'deflate',
});
return raw.pipe(zlib.createDeflate()).pipe(resp);
}
resp.writeHead(200, {
'Content-Type': contentType,
});
raw.pipe(resp);
// fs.readFile(realpath, function cb (err, data) {
// console.log(data.length);
// resp.writeHead(200, {
// 'Content-Type': contentType,
// });
// resp.write(data, "binary");
// resp.end();
// });
});
});
server.listen(PORT);
// [Node.js静态文件服务器](http://lib.csdn.net/article/nodejs/10831)
1.1.2. mime.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"
};
1.1.3. config
- 为指定几种后缀的文件,在响应时添加 Expires 头和 Cache-Control: max-age 头。超时日期设置为1年。
- 由于是静态文件服务器,为所有请求,响应时返回 Last-Modified 头。
- 为带 If-Modified-Since 的请求头,做日期检查,如果没有修改,则返回304。若修改,则返回文件。
exports.Expires = {
fileMatch: /^(gif|png|jpg|js|css)$/ig,
maxAge: 60 * 60 * 24 * 355,
};
exports.Compress = {
match: /css|js|html/ig,
};
- 浏览器在发送请求之前由于检测到 Cache-Control 和 Expires(Cache-Control 的优先级高于 Expires,但有的浏览器不支持 Cache-Control,这时采用 Expires),如果没有过期,则不会发送请求,而直接从缓存中读取文件。
- 我们同时也要检测浏览器是否发送了 If-Modified-Since 请求头。如果发送而且跟文件的修改时间相同的话,返回304状态。