zoukankan      html  css  js  c++  java
  • nodejs入门-静态文件服务器

    本文展示是基于node.js的静态文件服务器,代码参考自这里,主要是练习node http、文件模块的使用,另外,对理解http协议也很有帮助
    除了实现了基本的路由控制,还实现了MIME类型、304缓存、gzip压缩、目录读取

    首先是配置文件,setting.js

    var setting = {
      webroot : '/xxx/xxx/webroot',
      viewdir : false,
      index : 'index.html',	//只有当viewdir为false时,此设置才有用
      expires : {
        filematch : /^(gif|png|jpg|js|css)$/ig,
        maxAge: 60 * 60 //默认为一个月
      },
      compress : {
        match : /css|js|html/ig
      }
    };
    module.exports = setting;

    MIME映射,mime.js

    var mime = {
      "html": "text/html",
      "ico": "image/x-icon",
      "css": "text/css",
      "gif": "image/gif",
      "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"
    };
    module.exports = mime;

    然后是主程序,server.js

    var http = require('http');
    var setting = require('./setting.js');
    var mime = require('./mime');
    var url = require('url');
    var util = require('util');
    var path = require('path');
    var fs = require('fs');
    var zlib = require('zlib');
    //访问统计
    var number = 0;
    var accesses = {};
     
    http.createServer(function(req, res){
      res.number = ++number;
      accesses[res.number] = {startTime : new Date().getTime()};
      var pathname = url.parse(req.url).pathname;
      //安全问题,禁止父路径
      pathname = pathname.replace(/../g, '');
      var realPath = setting.webroot + pathname;
      accesses[res.number].path = pathname;
      readPath(req, res, realPath, pathname);
    }).listen(8000);
     
    console.log('http server start at parth 8000
    
    
    ');
    //判断文件是否存在
    function readPath(req, res, realPath, pathname){
      //首先判断所请求的资源是否存在
      path.exists(realPath, function(ex){
        console.log('path.exists--%s', ex);
        if(!ex){
          responseWrite(res, 404, {'Content-Type' : 'text/plain'}, 
              'This request URL ' + pathname + ' was not found on this server.');
        }else{
          //文件类型
          fs.stat(realPath, function(err, stat){
            if(err){
              responseWrite(res, 500, err);
            }else{
              //目录
              if(stat.isDirectory()){
                //是否读取目录
                if(setting.viewdir){
                  fs.readdir(realPath, function(err, files){
                    if(err){
                      responseWrite(res, 500, err);
                    }else{
                      var htm = '<html><head><title>' + pathname + '</title></head><body>' + pathname + '<hr>';
                      for(var i = 0; i < files.length; i++){
                        htm += '<br><a href="' + pathname + (pathname.slice(-1) != '/' ? '/' : '') 
                            + files[i] + '">' + files[i] + '</a>', 'utf8';
                      }
                      responseWrite(res, 200, {'Content-Type' : 'text/html'}, htm);
                    }
                  });
                }else if(setting.index && realPath.indexOf(setting.index) < 0){
                  readPath(req, res, path.join(realPath, '/', setting.index), path.join(pathname, '/', setting.index));
                }else{
                  responseWrite(res, 404, {'Content-Type' : 'text/plain'}, 
                      'This request URL ' + pathname + ' was not found on this server.');
                }
              }else{
                var type = path.extname(realPath);
                type = type ? type.slice(1) : 'nuknown';
                var header = {'Content-Type' : mime[type] || 'text/plain'};
                //缓存支持
                if(setting.expires && setting.expires.filematch 
                    && type.match(setting.expires.filematch)){
                  var expires = new Date(),
                  maxAge = setting.expires.maxAge || 3600 * 30;
                  expires.setTime(expires.getTime() + maxAge * 1000);
                  header['Expires'] = expires.toUTCString();
                  header['Cache-Control'] = 'max-age=' + maxAge;
                  var lastModified = stat.mtime.toUTCString();
                  header['Last-Modified'] = lastModified;
                  //判断是否304
                  if(req.headers['if-modified-since'] && lastModified == req.headers['if-modified-since']){
                    responseWrite(res, 304, 'Not Modified');
                  }else{
                    readFile(req, res, realPath, header, type);
                  }
                }else{
                  readFile(req, res, realPath, header, type);
                }
              }
            }
          });
        }
      });
    }
    //读文件/压缩/输出
    function readFile(req, res, realPath, header, type){
      var raw = fs.createReadStream(realPath), cFun;
      //是否gzip
      if(setting.compress && setting.compress.match 
          && type.match(setting.compress.match) && req.headers['accept-encoding']){
        if(req.headers['accept-encoding'].match(/gzip/)){
          header['Content-Encoding'] = 'gzip';
          cFun = 'createGzip';
        }else if(req.headers['accept-encoding'].match(/deflate/)){
          header['Content-Encoding'] = 'deflate';
          cFun = 'createDeflate';
        }
      }
      res.writeHead(200, header);
      if(cFun){
        raw.pipe(zlib[cFun]()).pipe(res);
      }else{
        raw.pipe(res);
      }
    }
    //普通输出
    function responseWrite(res, starus, header, output, encoding){
      encoding = encoding || 'utf8';
      res.writeHead(starus, header);
      if(output){
        res.write(output, encoding);
      }
      res.end();
      accesses[res.number].endTime = new Date().getTime();
      //日志输出
      console.log('access[%s]--%s--%s--%s--%s
    
    ', res.number, accesses[res.number].path, 
          (accesses[res.number].endTime - accesses[res.number].startTime), 
          starus, (output ? output.length : 0));
     delete accesses[res.number];
    }

    over!
    尚欠缺的功能:日志记录、断点、容错等~~以后有时间再加啦

  • 相关阅读:
    node.js基础回顾
    PHP基础回顾之表单(二)
    PHP基础回顾(一)
    知识图谱Knowledge Graph
    Qt addStretch()详解
    Qt实现 QQ好友列表QToolBox
    Qt5
    用户级线程和内核级线程
    TCP状态转换图、滑动窗口、半连接状态、2MSL
    理解tcp顺序释放操作和tcp的半关闭
  • 原文地址:https://www.cnblogs.com/sunwubin/p/3431686.html
Copyright © 2011-2022 走看看