UNPKG

rrestjs

Version:

HIgh performance node.js ROA & RESTFUL web framework.

147 lines (143 loc) 5.77 kB
/* *RestStatic.js 是rrestjs框架静态文件输出的模块 * *为res.sendfile提供了支持,主要功能是设置响应头,并且响应静态文件 * *增加了静态文件的2次状态缓存,加速响应,减少i/o操作 * *exports fn(res, path, cb) */ var fs = require('fs'), path = require('path'), RestUtils = require('./RestUtils'), Buffer = require('buffer').Buffer, mime = require('mime'), restzlib = require('./RestZlib'), msg = require('./msg/msg'), RestStatic = {//读取配置文件 option:{ getOnly : _restConfig.staticGetOnly, maxAge : _restConfig.staticMaxAge, }, mtimeBuffer:{},//2级缓存,减少一次i/o操作 Lv2MaxAge : _restConfig.staticLv2MaxAge,//2级缓存存在时间 zlibArray : _restConfig.ZlibArray, //选择性gzip输出 }; RestStatic.setHeaders = function(res, option){ //设置各种静态响应头 var charset = mime.charsets.lookup(option.type); if (!res.getHeader('Date')) res.setHeader('Date', new Date().toUTCString()); if (!res.getHeader('Cache-Control')) res.setHeader('Cache-Control', 'public, max-age=' + (option.maxAge / 1000)); if (!res.getHeader('Last-Modified')) res.setHeader('Last-Modified', option.mtime.utc); if (!res.getHeader('Content-Type')) res.setHeader('Content-Type', option.type + (charset ? '; charset=' + charset : '')); if (!res.getHeader('Etag')) res.setHeader('Etag', option.mtime.utcmd5); res.setHeader('Accept-Ranges', 'bytes'); }; RestStatic.sendfiles = function(req, res, filepath, stype, cb){ //输出文件的方法 var acceptEncoding = req.headers['accept-encoding'], //获取客户端是否支持gzip type = restzlib.check(acceptEncoding), stype = stype, canZlib = RestStatic.zlibArray.some(function(value){//判断文件类型是否在gizp输出数组中 return value == stype; }); fs.readFile(filepath, function(err, data){ if(err){ //如果错误,则输出404 RestUtils.errorRes404(res, msg.resmsg.notFound); return cb(err); } if(type && canZlib) restzlib.send(res, data, type); //gzip输出 else{ res.setHeader('Content-Length', data.length); //正常输出 data = RestUtils.headReq(req, data);//如果是head请求,则值响应http头,返回空 res.end(data); } cb(); }); }; RestStatic.ranges = function(res, ranges, stats, cb){ //ranges支持 var len = stats.size, ranges = RestUtils.parseRange(len, ranges, cb), opts = {}; if (ranges) { opts.start = ranges[0].start; opts.end = ranges[0].end; len = opts.end - opts.start + 1; res.statusCode = 206; res.setHeader('Content-Range', 'bytes ' + opts.start + '-' + opts.end + '/' + stats.size); } else { cb('Range error:'+RestUtils.error(416)); return false; } return true; } RestStatic.ismodify = function(req, res, option, stats, filepath){ //判断是否让浏览器使用缓存 if(res._nocache) return false;//如果手动设置res.cache(false)或者res.cache('public',-1)则表示不使用缓存 var UTC = stats.mtime instanceof Date ? stats.mtime.toUTCString() : stats.mtime.utc option.mtime = { utc : UTC, utcmd5 : RestUtils.md5(UTC) }; RestStatic.setHeaders(res, option); RestStatic.mBuffer(filepath, option.mtime); //更新缓存或建立缓存 if(RestUtils.conditionalGET(req) && RestUtils.modified(req, res)){ RestUtils.notModified(res, option) return true; }//如果已经缓存 else return false; } RestStatic.send = module.exports = function(res, filepath, cb){ //核心函数,主入口 var option = RestUtils.merge({}, RestStatic.option), cb = cb || function(){}, req = res._restReq, mBuffer = RestStatic.mtimeBuffer, stype = option.type = mime.lookup(filepath), mtime; if(option.getOnly && 'GET' != req.method && 'HEAD' != req.method){ var errstr = msg.resmsg.getOnlyError; RestUtils.forbidden(res, errstr); return cb(errstr); }//如果不是get请求,则禁止 if (mtime = mBuffer[filepath]){//如果找到2级缓存 if(RestStatic.ismodify(req, res, option, {mtime : mtime}, filepath)) return cb(); //响应304,客户端读取缓存 RestStatic.sendfiles(req, res, filepath, stype, cb); //输出文件 } else{ //如果没有找到2级缓存 fs.stat(filepath, function(err, stats){ if (err){ RestUtils.errorRes404(res, msg.resmsg.notFound); return cb(err); } else if (stats.isDirectory()){ RestUtils.errorRes404(res, msg.resmsg.notFound); return cb(msg.errmsg.staticSendError); } if(RestStatic.ismodify(req, res, option, stats, filepath)) return cb(); //获取了状态判断是否缓存 var ranges = req.headers.range; if(ranges){//如果是ranges if(RestStatic.ranges(res, ranges, stats, cb)) RestStatic.sendfiles(req, res, filepath, stype, cb);//如果ranges合法,则输出 } else RestStatic.sendfiles(req, res, filepath, stype, cb);//如果不是ranges,则直接输出 }); } }; RestStatic.mBuffer = function(key, mtime){ var mBuffer = RestStatic.mtimeBuffer; if(!mBuffer[key] && (++mBuffer.length > _restConfig.staticLv2Number)){ RestStatic.clearmBuffer();//如果mBuffer不存在,并且长度已经超过1000了,清空2级缓存Buffer } mBuffer[key] = mtime; }; RestStatic.clearmBuffer = function(){//清除2级缓存的方法 RestStatic.mtimeBuffer = { length:0, } }; RestStatic.loopClear = function(){ //定期清空2级缓存 setInterval(function(){ RestStatic.clearmBuffer(); },RestStatic.Lv2MaxAge) }();