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”
};
所有文件放同一个目录。
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/');
});
解释
- 引入必要的模块:
http
,url
,path
, 和fs
。 - 定义 MIME 类型:将常见的文件扩展名映射到相应的 MIME 类型。
- 创建HTTP服务器:
- 解析请求URL以获取文件路径。
- 检查文件是否存在。
- 设置响应头,包括内容类型、最后修改时间等。
- 处理304状态码(如果资源未被修改)。
- 处理范围请求(例如,部分下载)。
- 启动服务器:监听端口8000并输出服务器运行信息。
此代码实现了基本的静态文件服务器功能,并支持范围请求和缓存控制。