Nodejs实现静态服务器

文件结构:

assets文件夹存放请求的文件

app.js文件实现服务器,运行时执行node app

浏览器在3000端口请求url为http://localhost:3000/app.html

//nodejs实现一个静态文件系统
var url = require('url');
var http = require('http');
var fs = require('fs');  //涉及文件读取
var path = require('path');  //涉及路径处理
var mime = require('./mime').types; //引入mime类型
var config = require('./config');

var server = http.createServer(function(request, response) {
   //获得请求url并得到最终的路径
    var pathname = url.parse(request.url).pathname;
   //文件路径前缀为assets的具体路径
    var realPath = "/Users/cmy/Desktop/nodeserver/assets" + pathname;
    
    //通过fs模块的fs.access方法来判断静态文件是否存在
    fs.access(realPath, function (error) {
        if (!error) {
            //获得文件扩展名,通过slice方法来剔除掉”.”,没有后缀名的文件认为是unknown
            var ext = path.extname(realPath);
            ext = ext ? ext.slice(1) : 'unknown';

            //设置contentType类型,支持多种mime类型
            var contentType = mime[ext] || 'text/plain';
            response.setHeader('Content-Type',contentType);

            //设置缓存支持和控制,提高性能,减少IO操作
            fs.stat(realPath,function(err,stats){
            	//stats.mtime表示文件最后一次被修改的时间。
                var lastModified = stats.mtime.toUTCString();
                var ifModifiedSince = 'If-Modified-Since'.toLowerCase();
                response.setHeader('Last-Modified',lastModified);

                //为指定的某几种后缀的文件设置有效时间为1小时,在config中配置
                if(ext.match(config.Expires.fileMatch)){
                    var expires = new Date();
                    //设置过期时间为当前时间加上maxage时间
                    //toUTCString() 方法可根据世界时 (UTC) 把 Date 对象转换为字符串,并返回结果。
                    //setTime和getTime都是以毫秒数设置或获取时间
                    //max-age的单位是秒
                    expires.setTime(expires.getTime() + config.Expires.maxAge * 1000);
                    response.setHeader("Expires", expires.toUTCString());
                    response.setHeader("Cache-Control", "max-age=" + config.Expires.maxAge);
                }

                //判断请求头部的是否存在ifModifiedSince,并判断lastModified是否等于ifModifiedSince
                //如果相等则说明文件没有修改,返回304
                if(request.headers[ifModifiedSince] && lastModified == request.headers[ifModifiedSince]){
                    response.writeHead(304,'Not Modified');
                    response.end();
                }else{
                //如果修改过则读取文件
                    fs.readFile(realPath, "binary", function(err, file) {
                        if (err) {
                            response.writeHead(500, {'Content-Type': 'text/plain'});
                            response.end(err);
                        } else {
                            response.writeHead(200, {'Content-Type': 'text/html'});
                            //response.write首次被调用时,会发送缓冲的响应头信息和响应主体的第一块数据到客户端
                            response.write(file, "binary");
                            response.end();
                        }
                     });
                }
            });
            
        }else{
            //如果文件不存在返回404
            response.writeHead(404, {'Content-Type': 'text/plain'});
            response.write("This request URL " + pathname + " was not found on this server.");
            response.end();       	
        }
    });
});

server.listen(3000);
console.log('listenning at 3000');

添加gzip压缩功能和路径判断补充功能的app.js

var url = require('url');
var http = require('http');
var fs = require('fs');  //涉及文件读取
var path = require('path');  //涉及路径处理
var mime = require('./mime').types; //引入mime类型
var config = require('./config');
var zlib = require("zlib");  //使用gzip压缩,引入原生模块zlib

var server = http.createServer(function(request, response) {
	//获得请求url并得到最终的路径
    var pathname = url.parse(request.url).pathname;
    
    ///结尾的请求,自动添加上”index.html
    if (pathname.slice(-1) === "/") {
        pathname = pathname + config.Welcome.file;
    }

    //使用normalize方法来处理掉不正常的/
    var realPath = path.join("/Users/cmy/Desktop/nodeserver/assets", path.normalize(pathname.replace(/\.\./g, "")));
    
    var pathHandle = function (realPath) {
    	//使用fs.stat处理路径
        fs.stat(realPath, function (err, stats) {
            if (err) {
                response.writeHead(404, "Not Found", {'Content-Type': 'text/plain'});
                response.write("This request URL " + pathname + " was not found on this server.");
                response.end();
            } else { //如果请求的路径没有以/结尾,需要做判断,看路径是目录还是文件
                if (stats.isDirectory()) {
                	//如果是目录则添加上/和index.html
                    realPath = path.join(realPath, "/", config.Welcome.file);
                    pathHandle(realPath);
                } else {
                	//获得文件扩展名,通过slice方法来剔除掉”.”,没有后缀名的文件认为是unknown
                    var ext = path.extname(realPath);
                    ext = ext ? ext.slice(1) : 'unknown';
                    var contentType = mime[ext] || "text/plain";
                    response.setHeader("Content-Type", contentType);

                    //设置缓存支持和控制,提高性能,减少IO操作
                    var lastModified = stats.mtime.toUTCString();
                    var ifModifiedSince = "If-Modified-Since".toLowerCase();
                    response.setHeader("Last-Modified", lastModified);

                    if (ext.match(config.Expires.fileMatch)) {
                        var expires = new Date();
                        expires.setTime(expires.getTime() + config.Expires.maxAge * 1000);
                        response.setHeader("Expires", expires.toUTCString());
                        response.setHeader("Cache-Control", "max-age=" + config.Expires.maxAge);
                    }

                    //判断请求头部的是否存在ifModifiedSince,并判断lastModified是否等于ifModifiedSince
                    //如果相等则说明文件没有修改,返回304
                    if (request.headers[ifModifiedSince] && lastModified == request.headers[ifModifiedSince]) {
                        response.writeHead(304, "Not Modified");
                        response.end();
                    } else {
                        //如果修改过则读取文件
                        //为了防止大文件,也为了满足zlib模块的调用模式,将读取文件改为流的形式进行读取。
                        //对于支持压缩的文件格式以及浏览器端接受gzip或deflate压缩,我们调用压缩。
                        //若不,则管道方式转发给response。
                        var raw = fs.createReadStream(realPath);
                        var acceptEncoding = request.headers['accept-encoding'] || "";
                        var matched = ext.match(config.Compress.match);
                        if (matched && acceptEncoding.match(/\bgzip\b/)) {
                            response.writeHead(200, "Ok", {'Content-Encoding': 'gzip'});
                            raw.pipe(zlib.createGzip()).pipe(response);
                        } else if (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(3000);
console.log('listenning at 3000');

 

config.js 和 mime.js


//指定后缀文件和过期日期,config.js
exports.Expires = {
	fileMatch: /^(gif|png|jpg|js|css)$/ig,
	maxAge: 60*60
}

//对于图片一类的文件,不需要进行gzip压缩,只压缩三种文件
exports.Compress = {
    match: /css|js|html/ig
};


//用户请求了一个目录路径,而且没有带上/。那么我们为其添加上/index.html,再重新做解析
exports.Welcome = {
    file: "index.html"
};




//content-type对应类型,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"
};

转载自:https://www.toolmao.com/nodejs-static-server

猜你喜欢

转载自blog.csdn.net/qq_30422457/article/details/82497468