如何使用Node.js创建HTTP文件服务器
Node.js作为基于Chrome V8引擎的JavaScript运行时环境,以其非阻塞I/O和事件驱动特性成为构建高性能网络应用的理想选择。本文将详细介绍如何利用Node.js内置的HTTP模块和文件系统模块(fs)创建功能完整的文件服务器,涵盖基础实现、进阶功能、安全优化及性能调优等核心内容。
一、基础HTTP服务器搭建
Node.js的http模块提供了创建Web服务器的底层能力。以下代码展示如何启动一个监听8080端口的HTTP服务器:
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
});
server.listen(8080, () => {
console.log('Server running at http://localhost:8080/');
});
这段代码创建了最基本的HTTP服务器,但尚未实现文件服务功能。我们需要结合fs模块读取文件系统内容。
二、文件服务核心实现
完整的文件服务器需要处理路径解析、文件读取和MIME类型映射。以下是分步骤实现:
1. 路径处理与安全校验
使用path模块规范化请求路径,防止目录遍历攻击:
const path = require('path');
const fs = require('fs');
function getSafePath(reqUrl, rootDir = './public') {
const parsedUrl = new URL(reqUrl, `http://${req.headers.host}`);
let filepath = decodeURIComponent(parsedUrl.pathname);
// 规范化路径并限制在根目录下
filepath = path.normalize(path.join(rootDir, filepath));
if (!filepath.startsWith(path.join(process.cwd(), rootDir))) {
throw new Error('Access denied');
}
return filepath;
}
2. MIME类型映射
建立文件扩展名到MIME类型的映射表:
const mimeTypes = {
'.html': 'text/html',
'.js': 'text/javascript',
'.css': 'text/css',
'.json': 'application/json',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.gif': 'image/gif',
'.svg': 'image/svg+xml',
'.wav': 'audio/wav',
'.mp4': 'video/mp4',
'.woff': 'application/font-woff',
'.ttf': 'application/font-ttf',
'.eot': 'application/vnd.ms-fontobject',
'.otf': 'application/font-otf',
'.wasm': 'application/wasm'
};
3. 完整文件服务逻辑
结合上述组件实现文件读取和响应:
function serveFile(req, res, filepath) {
fs.stat(filepath, (err, stats) => {
if (err) {
if (err.code === 'ENOENT') {
res.writeHead(404);
return res.end('404 Not Found');
}
res.writeHead(500);
return res.end('Internal Server Error');
}
if (stats.isDirectory()) {
// 如果是目录,默认返回index.html或列出文件
const indexPath = path.join(filepath, 'index.html');
fs.access(indexPath, fs.constants.F_OK, (err) => {
if (!err) {
serveFile(req, res, indexPath);
} else {
listDirectory(req, res, filepath);
}
});
return;
}
// 获取文件扩展名对应的MIME类型
const ext = path.parse(filepath).ext;
const contentType = mimeTypes[ext] || 'application/octet-stream';
// 创建可读流处理大文件
const readStream = fs.createReadStream(filepath);
res.writeHead(200, { 'Content-Type': contentType });
readStream.pipe(res);
readStream.on('error', (err) => {
res.writeHead(500);
res.end('File Reading Error');
});
});
}
function listDirectory(req, res, dirpath) {
fs.readdir(dirpath, (err, files) => {
if (err) {
res.writeHead(500);
return res.end('Directory Listing Error');
}
let html = 'Directory Listing
';
files.forEach(file => {
const stat = fs.statSync(path.join(dirpath, file));
const isDir = stat.isDirectory() ? '/' : '';
html += `- ${file}${isDir}
`;
});
html += '
';
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(html);
});
}
三、完整服务器实现
将上述组件整合为完整服务器:
const http = require('http');
const path = require('path');
const fs = require('fs');
const PORT = 8080;
const ROOT_DIR = './public';
const mimeTypes = { /* 同上 */ };
const server = http.createServer((req, res) => {
try {
const filepath = getSafePath(req.url, ROOT_DIR);
serveFile(req, res, filepath);
} catch (err) {
res.writeHead(403);
res.end('Forbidden');
}
});
server.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}/`);
});
四、进阶功能实现
1. 缓存控制
通过Last-Modified和ETag实现缓存:
function serveFileWithCache(req, res, filepath) {
fs.stat(filepath, (err, stats) => {
// ...原有错误处理...
const lastModified = stats.mtime.toUTCString();
const etag = stats.size + '-' + Number(stats.mtime);
// 检查客户端缓存
const ifModifiedSince = req.headers['if-modified-since'];
const ifNoneMatch = req.headers['if-none-match'];
if (ifModifiedSince && ifNoneMatch) {
if (ifModifiedSince === lastModified && ifNoneMatch === etag) {
res.writeHead(304);
return res.end();
}
}
// 设置缓存头
res.setHeader('Last-Modified', lastModified);
res.setHeader('ETag', etag);
res.setHeader('Cache-Control', 'public, max-age=86400'); // 24小时缓存
// ...原有文件服务逻辑...
});
}
2. 压缩支持
使用zlib模块实现Gzip压缩:
const zlib = require('zlib');
function serveCompressed(req, res, filepath) {
const acceptEncoding = req.headers['accept-encoding'] || '';
const canGzip = acceptEncoding.includes('gzip');
fs.readFile(filepath, (err, data) => {
if (err) { /* 错误处理 */ }
const ext = path.parse(filepath).ext;
const contentType = mimeTypes[ext] || 'application/octet-stream';
if (canGzip) {
res.writeHead(200, {
'Content-Type': contentType,
'Content-Encoding': 'gzip',
'Vary': 'Accept-Encoding'
});
zlib.gzip(data, (err, compressed) => {
if (err) { /* 错误处理 */ }
res.end(compressed);
});
} else {
res.writeHead(200, { 'Content-Type': contentType });
res.end(data);
}
});
}
3. 范围请求支持
实现视频/大文件的分块传输:
function serveRange(req, res, filepath) {
const range = req.headers.range;
if (!range) {
return serveFile(req, res, filepath); // 无range头则正常传输
}
fs.stat(filepath, (err, stats) => {
if (err) { /* 错误处理 */ }
const fileSize = stats.size;
const [start, end] = range.replace(/bytes=/, '').split('-');
const realStart = parseInt(start, 10);
const realEnd = end ? parseInt(end, 10) : fileSize - 1;
const chunkSize = (realEnd - realStart) + 1;
const ext = path.parse(filepath).ext;
const contentType = mimeTypes[ext] || 'application/octet-stream';
res.writeHead(206, {
'Content-Range': `bytes ${realStart}-${realEnd}/${fileSize}`,
'Accept-Ranges': 'bytes',
'Content-Length': chunkSize,
'Content-Type': contentType
});
const stream = fs.createReadStream(filepath, { start: realStart, end: realEnd });
stream.pipe(res);
});
}
五、安全优化
1. 请求限制
防止DDoS攻击和过大请求:
const MAX_BODY_SIZE = 1024 * 1024; // 1MB
server.on('request', (req, res) => {
let body = [];
req.on('data', chunk => {
body.push(chunk);
if (Buffer.concat(body).length > MAX_BODY_SIZE) {
req.destroy(); // 终止过大请求
res.writeHead(413);
return res.end('Payload Too Large');
}
});
// ...原有处理逻辑...
});
2. HTTPS支持
使用SSL/TLS加密通信:
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.cert')
};
https.createServer(options, (req, res) => {
// 原有HTTP处理逻辑
}).listen(443);
3. CORS配置
跨域资源共享设置:
function setCORSHeaders(res, allowedOrigins = ['*']) {
const origin = req.headers.origin;
if (allowedOrigins.includes(origin) || allowedOrigins.includes('*')) {
res.setHeader('Access-Control-Allow-Origin', origin || '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
}
}
六、性能优化
1. 集群模式
利用多核CPU:
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log(`Master ${process.pid} is running`);
for (let i = 0; i {
console.log(`Worker ${worker.process.pid} died`);
cluster.fork();
});
} else {
// 工作进程代码(原服务器代码)
const server = http.createServer(/*...*/);
server.listen(8080);
}
2. 静态文件中间件
使用express.static简化实现:
const express = require('express');
const app = express();
app.use(express.static('public', {
dotfiles: 'ignore',
etag: true,
lastModified: true,
maxAge: '1d',
redirect: false
}));
app.listen(8080);
七、完整示例项目结构
project/
├── public/ # 静态文件目录
│ ├── index.html
│ ├── style.css
│ └── script.js
├── server.js # 主服务器文件
├── certs/ # SSL证书
│ ├── server.key
│ └── server.cert
└── package.json
八、部署建议
1. 使用PM2进行进程管理:
npm install -g pm2
pm2 start server.js --name "file-server" -i max
2. 配置Nginx反向代理:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
}
}
关键词:Node.js、HTTP服务器、文件服务、MIME类型、路径安全、缓存控制、压缩传输、范围请求、集群模式、静态文件中间件
简介:本文系统讲解了使用Node.js创建HTTP文件服务器的完整方案,涵盖基础实现、安全防护、性能优化等核心模块,包含路径处理、MIME类型映射、缓存控制、Gzip压缩、范围请求等关键技术,并提供HTTPS、CORS、集群模式等进阶配置方法,最终给出完整的项目结构和部署建议。