Nodejs 静态文件服务器代码整理

Nodejs 静态文件服务器代码整理

staticServer.js文件

var sys = require(‘sys’),
http = require(‘http’),
url = require(‘url’),
path = require(‘path’),
fs = require(‘fs’),
mime = require(’./mime’).types,
config = require(’./config’),
zlib = require(‘zlib’),
utils = require(’./utils’);

http.createServer(function (request, response) {
var uri = url.parse(request.url).pathname;
var filename = path.join(__dirname + ‘/static’, uri);

//response.writeHead(200, { "Content-Type": "text/plain" });  
//response.write(filename);  
//response.end();  

var compressHandle = function (raw, compressMatched, statusCode, reasonPhrase) {
var stream = raw;
var acceptEncoding = request.headers[‘accept-encoding’] || “”;
var matched = compressMatched; // ext.match(config.Compress.match);

if (matched && acceptEncoding.match(/\bgzip\b/)) {  

    response.setHeader("Content-Encoding", "gzip");  
    stream = raw.pipe(zlib.createGzip());  

} else if (matched && acceptEncoding.match(/\bdeflate\b/)) {  

    response.setHeader("Content-Encoding", "deflate");  
    stream = raw.pipe(zlib.createDeflate());  
}  

response.writeHead(statusCode, reasonPhrase);  
stream.pipe(response);  

};

path.exists(filename, function (exists) {
if (!exists) {
response.writeHead(404, { Content-Type”: text/plain });
response.write(“404 Not Found\n”);
response.end();
return;
}

var ext = path.extname(filename);  
ext = ext ? ext.slice(1) : 'unknow';  
var contentType = mime[ext] || 'text/plain';  
response.setHeader('Content-Type', contentType);  

fs.stat(filename, function (err, stat) {  
    var lastModified = stat.mtime.toUTCString();  
    var ifModifiedSince = "If-Modified-Since".toLowerCase();  

    response.setHeader("Last-Modified", lastModified);  
    response.setHeader("Server", "Node/V2");  

    // add cache control  
    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);  
    }  

    if (request.headers[ifModifiedSince] && lastModified == request.headers[ifModifiedSince]) {  
        response.writeHead(304, 'Not Modified');  
        response.end();  
        return;  
    }  
    /* 
    fs.readFile(filename, "binary", function (err, file) { 
    if (err) { 
    response.writeHead(500, 'Internal server error', { 'Content-Type': 'text/plain' }); 
    response.write(err + "\n"); 
    response.end(); 
    return; 
    } 
    response.writeHead(200, 'OK'); 
    response.write(file, 'binary'); 
    response.end(); 
    });*/  

    var compressMatched = ext.match(config.Compress.match);  

    if (request.headers["range"]) {  
        var range = utils.parseRange(request.headers["range"], stat.size);  
        if (range) {  
            response.setHeader("Content-Range", "bytes " + range.start + "-" + range.end + "/" + stat.size);  
            response.setHeader("Content-Length", (range.end - range.start + 1));  
            var raw = fs.createReadStream(filename, { "start": range.start, "end": range.end });  
            compressHandle(raw, compressMatched, 206, "Partial Content");  
        }  
        else {  
            response.removeHeader("Content-Length");  
            response.writeHead(416, "Request Range Not Satisfiable");  
            response.end();  
        }  
    }  
    else {  
        var raw = fs.createReadStream(filename);  
        compressHandle(raw, compressMatched, 200, "OK");  
    }  
});  

});

}).listen(8000);

sys.puts(‘server running at http://localhost:8000/’);

config.js文件

exports.Expires = {
fileMatch: /^(gif|png|jpg|js|css)$/ig,
maxAge: 60 * 60 * 24 * 365
};

exports.Compress = {
match: /css|js|html/ig
};

utils.js文件

exports.parseRange = function (str, size) {
if (str.indexOf(",") != -1) {
return;
}

var range = str.split("-");  
    start = parseInt(range[0], 10),  
    end = parseInt(range[1], 10);  

// Case: -100
if (isNaN(start)) {
start = size - end;
end = size - 1;
}
else if (isNaN(end)) {
// Case: 100-
end = size - 1;
}

// Invalid
if (isNaN(start) || isNaN(end) || start > end || end > size) {
return;
}

return {start: start, end: end};

}

utils.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”,
“txt”: “text/plain”,
“xml”: “text/xml”,
“swf”: “application/x-shockwave-flash”,
“wav”: “audio/x-wav”,
“wma”: “audio/x-ms-wma”,
“wmv”: “video/x-ms-wmv”,
“svg”: “image/svg+xml”
};

所有文件放同一个目录。

3 回复

Node.js 静态文件服务器代码整理

staticServer.js 文件

const http = require('http');
const url = require('url');
const path = require('path');
const fs = require('fs');
const mime = require('./mime').types;
const config = require('./config');
const zlib = require('zlib');
const utils = require('./utils');

http.createServer((request, response) => {
    const uri = url.parse(request.url).pathname;
    const filename = path.join(__dirname + '/static', uri);

    const compressHandle = (raw, compressMatched, statusCode, reasonPhrase) => {
        let stream = raw;
        const acceptEncoding = request.headers['accept-encoding'] || "";

        if (compressMatched && acceptEncoding.includes('gzip')) {
            response.setHeader("Content-Encoding", "gzip");
            stream = raw.pipe(zlib.createGzip());
        } else if (compressMatched && acceptEncoding.includes('deflate')) {
            response.setHeader("Content-Encoding", "deflate");
            stream = raw.pipe(zlib.createDeflate());
        }

        response.writeHead(statusCode, reasonPhrase);
        stream.pipe(response);
    };

    fs.exists(filename, exists => {
        if (!exists) {
            response.writeHead(404, {"Content-Type": "text/plain"});
            response.write("404 Not Found\n");
            response.end();
            return;
        }

        const ext = path.extname(filename);
        const contentType = mime[ext.slice(1)] || 'text/plain';
        response.setHeader('Content-Type', contentType);

        fs.stat(filename, (err, stat) => {
            const lastModified = stat.mtime.toUTCString();
            const ifModifiedSince = "If-Modified-Since".toLowerCase();

            response.setHeader("Last-Modified", lastModified);
            response.setHeader("Server", "Node/V2");

            if (ext.match(config.Expires.fileMatch)) {
                const 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}`);
            }

            if (request.headers[ifModifiedSince] === lastModified) {
                response.writeHead(304, 'Not Modified');
                response.end();
                return;
            }

            const compressMatched = ext.match(config.Compress.match);

            if (request.headers["range"]) {
                const range = utils.parseRange(request.headers["range"], stat.size);
                if (range) {
                    response.setHeader("Content-Range", `bytes ${range.start}-${range.end}/${stat.size}`);
                    response.setHeader("Content-Length", range.end - range.start + 1);
                    const raw = fs.createReadStream(filename, {start: range.start, end: range.end});
                    compressHandle(raw, compressMatched, 206, "Partial Content");
                } else {
                    response.removeHeader("Content-Length");
                    response.writeHead(416, "Request Range Not Satisfiable");
                    response.end();
                }
            } else {
                const raw = fs.createReadStream(filename);
                compressHandle(raw, compressMatched, 200, "OK");
            }
        });
    });
}).listen(8000);

console.log('Server running at http://localhost:8000/');

config.js 文件

module.exports = {
    Expires: {
        fileMatch: /^(gif|png|jpg|js|css)$/i,
        maxAge: 60 * 60 * 24 * 365
    },
    Compress: {
        match: /css|js|html/i
    }
};

utils.js 文件

module.exports = {
    parseRange: (str, size) => {
        if (str.includes(',')) {
            return;
        }

        const range = str.split("-");
        const start = parseInt(range[0], 10);
        const end = parseInt(range[1], 10);

        if (isNaN(start)) {
            start = size - end;
            end = size - 1;
        } else if (isNaN(end)) {
            end = size - 1;
        }

        if (isNaN(start) || isNaN(end) || start > end || end > size) {
            return;
        }

        return { start, end };
    },
    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",
        "txt": "text/plain",
        "xml": "text/xml",
        "swf": "application/x-shockwave-flash",
        "wav": "audio/x-wav",
        "wma": "audio/x-ms-wma",
        "wmv": "video/x-ms-wmv",
        "svg": "image/svg+xml"
    }
};

以上代码展示了如何使用 Node.js 创建一个简单的静态文件服务器,并支持文件缓存、压缩以及范围请求。所有的配置和辅助函数都放在单独的文件中,以保持代码的整洁和可维护性。


代码没有标记…

Node.js 静态文件服务器代码整理

以下是一个简单的Node.js静态文件服务器代码示例。该服务器会处理基本的请求,并提供静态文件服务,支持文件压缩(gzip/deflate)和缓存控制。

示例代码

staticServer.js

const http = require('http');
const url = require('url');
const path = require('path');
const fs = require('fs');
const mimeTypes = {
  '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',
  'txt': 'text/plain',
  'xml': 'text/xml',
  'swf': 'application/x-shockwave-flash',
  'wav': 'audio/x-wav',
  'wma': 'audio/x-ms-wma',
  'wmv': 'video/x-ms-wmv',
  'svg': 'image/svg+xml'
};

http.createServer((req, res) => {
  const uri = url.parse(req.url).pathname;
  const filename = path.join(process.cwd(), 'static', uri);

  fs.access(filename, fs.constants.F_OK, (err) => {
    if (err) {
      res.writeHead(404, { 'Content-Type': 'text/plain' });
      res.write('404 Not Found\n');
      res.end();
      return;
    }

    const ext = path.extname(filename);
    const contentType = mimeTypes[ext.slice(1)] || 'text/plain';

    fs.stat(filename, (err, stats) => {
      if (err) {
        res.writeHead(500, { 'Content-Type': 'text/plain' });
        res.write(err + '\n');
        res.end();
        return;
      }

      res.setHeader('Content-Type', contentType);
      res.setHeader('Last-Modified', stats.mtime.toUTCString());
      res.setHeader('Server', 'Node/V2');

      if (req.headers['if-modified-since'] && stats.mtime.toUTCString() === req.headers['if-modified-since']) {
        res.writeHead(304, 'Not Modified');
        res.end();
        return;
      }

      const range = req.headers.range;
      if (range) {
        const parts = range.replace(/bytes=/, '').split('-');
        const start = parseInt(parts[0], 10);
        const end = parts[1] ? parseInt(parts[1], 10) : stats.size - 1;

        if (start >= stats.size || end >= stats.size) {
          res.writeHead(416, { 'Content-Range': `bytes */${stats.size}` });
          res.end();
          return;
        }

        res.setHeader('Content-Range', `bytes ${start}-${end}/${stats.size}`);
        res.setHeader('Content-Length', end - start + 1);
        res.writeHead(206, 'Partial Content');

        const fileStream = fs.createReadStream(filename, { start, end });
        fileStream.pipe(res);
        return;
      }

      res.writeHead(200, 'OK');
      fs.createReadStream(filename).pipe(res);
    });
  });
}).listen(8000, () => {
  console.log('Server running at http://localhost:8000/');
});

解释

  1. 引入必要的模块http, url, path, 和 fs
  2. 定义 MIME 类型:将常见的文件扩展名映射到相应的 MIME 类型。
  3. 创建HTTP服务器
    • 解析请求URL以获取文件路径。
    • 检查文件是否存在。
    • 设置响应头,包括内容类型、最后修改时间等。
    • 处理304状态码(如果资源未被修改)。
    • 处理范围请求(例如,部分下载)。
  4. 启动服务器:监听端口8000并输出服务器运行信息。

此代码实现了基本的静态文件服务器功能,并支持范围请求和缓存控制。

回到顶部
AI 助手
你好,我是IT营的 AI 助手
您可以尝试点击下方的快捷入口开启体验!