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-ControlExpiresCache-Control 的优先级高于 Expires,但有的浏览器不支持 Cache-Control,这时采用 Expires),如果没有过期,则不会发送请求,而直接从缓存中读取文件。
  • 我们同时也要检测浏览器是否发送了 If-Modified-Since 请求头。如果发送而且跟文件的修改时间相同的话,返回304状态。

results matching ""

    No results matching ""