在 Node.js 中构建一个简单的静态文件 Web 服务器
在本文中,我们将构建一个静态文件 Web 服务器,它将列出目录中的所有文件,并在单击文件名时显示文件内容。创建静态文件服务器的步骤如下:
- 第 1 步:导入必要的模块,定义 MIME 类型,帮助浏览器了解正在发送的文件类型。
Javascript
// Importing necessary modules
const http = require('http');
const url = require('url');
const fs = require('fs');
const path = require('path');
// Port on which the server will create
const PORT = 1800;
// Maps file extension to MIME types which
// helps the browser to understand what to
// do with the file
const mimeType = {
'.ico': 'image/x-icon',
'.html': 'text/html',
'.js': 'text/javascript',
'.json': 'application/json',
'.css': 'text/css',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.wav': 'audio/wav',
'.mp3': 'audio/mpeg',
'.svg': 'image/svg+xml',
'.pdf': 'application/pdf',
'.doc': 'application/msword',
'.eot': 'application/vnd.ms-fontobject',
'.ttf': 'application/font-sfnt'
};
Javascript
// Creating a server and listening the port 1800
http.createServer( (req, res) => {
}).listen(PORT);
Javascript
// Parsing the requested URL
const parsedUrl = url.parse(req.url);
// If requested url is "/" like "http://localhost:8100/"
if(parsedUrl.pathname==="/") {
var filesLink="";
res.setHeader('Content-type', 'text/html');
var filesList=fs.readdirSync("./");
filesList.forEach(element => {
if(fs.statSync("./"+element).isFile()) {
filesLink +=`
-
${element}
` ;
}
});
filesLink+="
";
res.end("List of files:
" + filesLink);
}
Javascript
/* processing the requested file pathname to
avoid directory traversal like,
http://localhost:1800/../fileOutofContext.txt
by limiting to the current directory only */
const sanitizePath =
path.normalize(parsedUrl.pathname).replace(/^(\.\.[\/\\])+/, '');
let pathname = path.join(__dirname, sanitizePath);
Javascript
if(!fs.existsSync(pathname)) {
// If the file is not found, return 404
res.statusCode = 404;
res.end(`File ${pathname} not found!`);
}
else {
// Read file from file system limit to the
// current directory only.
fs.readFile(pathname, function(err, data) {
if(err) {
res.statusCode = 500;
res.end(`Error in getting the file.`);
}
else {
// Based on the URL path, extract the file
// extension. Ex .js, .doc, ...
const ext = path.parse(pathname).ext;
// If the file is found, set Content-type
// and send data
res.setHeader('Content-type',
mimeType[ext] || 'text/plain' );
res.end(data);
}
});
}
Javascript
/* Node.js static file web server */
// Importing necessary modules
const http = require('http');
const url = require('url');
const fs = require('fs');
const path = require('path');
// Port on which the server will create
const PORT = 1800;
// Maps file extension to MIME types which
// helps browser to understand what to do
// with the file
const mimeType = {
'.ico': 'image/x-icon',
'.html': 'text/html',
'.js': 'text/javascript',
'.json': 'application/json',
'.css': 'text/css',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.wav': 'audio/wav',
'.mp3': 'audio/mpeg',
'.svg': 'image/svg+xml',
'.pdf': 'application/pdf',
'.doc': 'application/msword',
'.eot': 'application/vnd.ms-fontobject',
'.ttf': 'application/font-sfnt'
};
// Creating a server and listening at port 1800
http.createServer( (req, res) => {
// Parsing the requested URL
const parsedUrl = url.parse(req.url);
// If requested url is "/" like "http://localhost:1800/"
if(parsedUrl.pathname==="/"){
var filesLink="";
res.setHeader('Content-type', 'text/html');
var filesList=fs.readdirSync("./");
filesList.forEach(element => {
if(fs.statSync("./"+element).isFile()){
filesLink+=`
-
${element}
` ;
}
});
filesLink+="
";
res.end("List of files:
" + filesLink);
}
/* Processing the requested file pathname to
avoid directory traversal like,
http://localhost:1800/../fileOutofContext.txt
by limiting to the current directory only. */
const sanitizePath =
path.normalize(parsedUrl.pathname).replace(/^(\.\.[\/\\])+/, '');
let pathname = path.join(__dirname, sanitizePath);
if(!fs.existsSync(pathname)) {
// If the file is not found, return 404
res.statusCode = 404;
res.end(`File ${pathname} not found!`);
}
else {
// Read file from file system limit to
// the current directory only.
fs.readFile(pathname, function(err, data) {
if(err){
res.statusCode = 500;
res.end(`Error in getting the file.`);
}
else {
// Based on the URL path, extract the
// file extension. Ex .js, .doc, ...
const ext = path.parse(pathname).ext;
// If the file is found, set Content-type
// and send data
res.setHeader('Content-type',
mimeType[ext] || 'text/plain' );
res.end(data);
}
});
}
}).listen(PORT);
console.log(`Server listening on port ${PORT}`);
- 第 2 步:在指定的端口(比如 1800)创建服务器。
Javascript
// Creating a server and listening the port 1800
http.createServer( (req, res) => {
}).listen(PORT);
- 第 3 步:我们将响应URL“/”以列出目录中的所有文件。我们将本文仅限于当前工作目录。将以下代码添加到服务器的函数调用中。
Javascript
// Parsing the requested URL
const parsedUrl = url.parse(req.url);
// If requested url is "/" like "http://localhost:8100/"
if(parsedUrl.pathname==="/") {
var filesLink="";
res.setHeader('Content-type', 'text/html');
var filesList=fs.readdirSync("./");
filesList.forEach(element => {
if(fs.statSync("./"+element).isFile()) {
filesLink +=`
-
${element}
` ;
}
});
filesLink+="
";
res.end("List of files:
" + filesLink);
}
- 第 4 步:预处理请求的文件路径名以避免目录遍历(如 http://localhost:1800/../fileOutofContext.txt),方法是将 '../' 替换为 ' '。
Javascript
/* processing the requested file pathname to
avoid directory traversal like,
http://localhost:1800/../fileOutofContext.txt
by limiting to the current directory only */
const sanitizePath =
path.normalize(parsedUrl.pathname).replace(/^(\.\.[\/\\])+/, '');
let pathname = path.join(__dirname, sanitizePath);
- 第五步:最后,检查文件是否存在。如果存在,则发送具有正确标头“内容类型”的文件,该文件具有根据与上述 MIME 类型映射的文件扩展名的值。否则,如果不存在则发送未找到文件!带有404状态码。
Javascript
if(!fs.existsSync(pathname)) {
// If the file is not found, return 404
res.statusCode = 404;
res.end(`File ${pathname} not found!`);
}
else {
// Read file from file system limit to the
// current directory only.
fs.readFile(pathname, function(err, data) {
if(err) {
res.statusCode = 500;
res.end(`Error in getting the file.`);
}
else {
// Based on the URL path, extract the file
// extension. Ex .js, .doc, ...
const ext = path.parse(pathname).ext;
// If the file is found, set Content-type
// and send data
res.setHeader('Content-type',
mimeType[ext] || 'text/plain' );
res.end(data);
}
});
}
完整代码:
Javascript
/* Node.js static file web server */
// Importing necessary modules
const http = require('http');
const url = require('url');
const fs = require('fs');
const path = require('path');
// Port on which the server will create
const PORT = 1800;
// Maps file extension to MIME types which
// helps browser to understand what to do
// with the file
const mimeType = {
'.ico': 'image/x-icon',
'.html': 'text/html',
'.js': 'text/javascript',
'.json': 'application/json',
'.css': 'text/css',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.wav': 'audio/wav',
'.mp3': 'audio/mpeg',
'.svg': 'image/svg+xml',
'.pdf': 'application/pdf',
'.doc': 'application/msword',
'.eot': 'application/vnd.ms-fontobject',
'.ttf': 'application/font-sfnt'
};
// Creating a server and listening at port 1800
http.createServer( (req, res) => {
// Parsing the requested URL
const parsedUrl = url.parse(req.url);
// If requested url is "/" like "http://localhost:1800/"
if(parsedUrl.pathname==="/"){
var filesLink="";
res.setHeader('Content-type', 'text/html');
var filesList=fs.readdirSync("./");
filesList.forEach(element => {
if(fs.statSync("./"+element).isFile()){
filesLink+=`
-
${element}
` ;
}
});
filesLink+="
";
res.end("List of files:
" + filesLink);
}
/* Processing the requested file pathname to
avoid directory traversal like,
http://localhost:1800/../fileOutofContext.txt
by limiting to the current directory only. */
const sanitizePath =
path.normalize(parsedUrl.pathname).replace(/^(\.\.[\/\\])+/, '');
let pathname = path.join(__dirname, sanitizePath);
if(!fs.existsSync(pathname)) {
// If the file is not found, return 404
res.statusCode = 404;
res.end(`File ${pathname} not found!`);
}
else {
// Read file from file system limit to
// the current directory only.
fs.readFile(pathname, function(err, data) {
if(err){
res.statusCode = 500;
res.end(`Error in getting the file.`);
}
else {
// Based on the URL path, extract the
// file extension. Ex .js, .doc, ...
const ext = path.parse(pathname).ext;
// If the file is found, set Content-type
// and send data
res.setHeader('Content-type',
mimeType[ext] || 'text/plain' );
res.end(data);
}
});
}
}).listen(PORT);
console.log(`Server listening on port ${PORT}`);
输出