静态文件服务器补充说明,使用Node.js搭建简单静态文件服务器

这半个多月一直在学Node.js,还是在入门阶段,不过已经对Node很感兴趣了。这里介绍一个简单的静态文件服务器,总结一下心得体会。为什么说简单呢,因为虽然基本功能都有,但是没加路由,还有没考虑一些安全性的东西。后面我会继续来总结完善。

本文参考了Node大神朴灵11年写的一篇博文(强烈建议读一读,虽然有一些接口有些老,但是搭建服务器的思路,各方面都有涉及),还有这位大神前辈的博客,以及Node.js 6.x版本的文档,stackoverflow一些答案。

如果哪里说得不对或者哪里有问题,请劳烦您指正,我会虚心接受。

正文如下:

一个Web服务器应具备以下几个功能:

1、能显示以.html/.htm结尾的Web页面

2、能直接打开以.js/.css/.json/.text结尾的文件内容

3、显示图片资源

4、自动下载以.apk/.docx/.zip结尾的文件

5、形如http://xxx.com/a/b/ , 则查找b目录下是否有index.html,如果有就显示,如果没有就列出该目录下的所有文件及文件夹,并可以进一步访问。

总体的思路如下:

1.先加载需要用到的几个模块 url(解析request,截取路径) path(解析路径) fs(文件读写操作) http(起服务器)

2.切出来请求的url和路径 (记得解码,防止中文乱码)

3.根据路径有无扩展名以及是否以“/”结尾作判断,重定向

4.根据路径来查找资源文件

OK,接下来上代码,我有详细的标注注释(咳,方便以后来查漏补缺)

前面有提到参考的那两篇博文的年代有些久远(其实也就4,5年前..),以致一些接口6.x版本的Node.js已经不支持了,或者一些方法近些年有了最佳实践。

说一下重构的地方:

1.判断路径类型,不使用fs.exist(),而是换成fs.stat方法

2.将回调换成了箭头函数。

3.我一直没查到getContentType这个方法.. 所以还是使用mime映射来传入content-type 其实已经专门有mime模块来处理这个问题了。

4.做了一下模块化处理

第一个模块: app.js 启动模块

"use strict";

//加载所需要的模块

var http = require('http');

var processRequest = require('./server');

//创建服务,这里很机智的把对response和request的处理封装成一个匿名函数,传入createServer中

//也可以直接在里面写,但是看起来不是很整洁

var httpServer = http.createServer((req, res) => {

processRequest(req, res);

});

var port = 8080;

//指定一个监听的接口

httpServer.listen(port, function() {

console.log(`app is running at port:${port}`);

});

第二个模块:mime.js 存放类型映射

module.exports = {

"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",

"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"

};

第三个模块,也是我们的核心模块 server.js

var url = require('url');

var fs = require('fs');

var path = require('path');

var mime = require('./mime');

function processRequest(request, response) {

//request里面切出标识符字符串

var requestUrl = request.url;

//url模块的parse方法 接受一个字符串,返回一个url对象,切出来路径

var pathName = url.parse(requestUrl).pathname;

//对路径解码,防止中文乱码

var pathName = decodeURI(pathName);

//解决301重定向问题,如果pathname没以/结尾,并且没有扩展名

if (!pathName.endsWith('/') && path.extname(pathName) === '') {

pathName += '/';

var redirect = "http://" + request.headers.host + pathName;

response.writeHead(301, {

location: redirect

});

//response.end方法用来回应完成后关闭本次对话,也可以写入HTTP回应的具体内容。

response.end();

};

//获取资源文件的绝对路径

var filePath = path.resolve(__dirname + pathName);

console.log(filePath);

//获取对应文件的文档类型

//我们通过path.extname来获取文件的后缀名。由于extname返回值包含”.”,所以通过slice方法来剔除掉”.”,

//对于没有后缀名的文件,我们一律认为是unknown。

var ext = path.extname(pathName);

ext = ext ? ext.slice(1) : 'unknown';

//未知的类型一律用"text/plain"类型

var contentType = mime[ext] || "text/plain";

fs.stat(filePath, (err, stats) => {

if (err) {

response.writeHead(404, { "content-type": "text/html" });

response.end("

404 Not Found

");

};

//没出错 并且文件存在

if (!err && stats.isFile()) {

response.writeHead(200, { "content-type": contentType });

//建立流对象,读文件

var stream = fs.createReadStream(filePath);

//错误处理

stream.on('error', function() {

response.writeHead(500, { "content-type": contentType });

response.end("

500 Server Error

");

});

//读取文件

stream.pipe(response);

//response.end(); 这个地方有坑,加了会关闭对话,看不到内容了

};

//如果路径是目录

if (!err && stats.isDirectory()) {

var html = "

";

//读取该路径下文件

fs.readdir(filePath, (err, files) => {

if (err) {

console.log("读取路径失败!");

} else {

// files.foreach(function (file) {

// //做成一个链接表,方便用户访问

// html+=`

`;

// });

for (var file of files) {

if (file === "index.html") {

response.writeHead(200, { "content-type": "text/html" });

response.end(file);

break;

};

html += `

`;

console.log(html);

}

response.writeHead(200, { "content-type": "text/html" });

response.end(html);

};

});

};

});

};

module.exports = processRequest;

这里有几个细节的地方值得注意,一个是如何判断并实现重定向的,第二个是如何来判断content-type的。第三个要注意异步回调的执行顺序问题,有个坑就在传html那里。

再说一个小坑吧,response.end()这个方法我理解的不够深,这个方法是用来关闭对话或者向response的body部分传内容,我把它放在了stream操作的下面,结果可想而知... 所以再涉及到文件读写时,一定要注意这个地方。

下面是实现截图:

1.这是我的目录,不用管那个vscode,那是visual studio code文件配置目录

![1.png](http://upload-images.jianshu.io/upload_images/3373032-ffc04d2fc246ad97.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

2.服务器启动在8080端口

![2.png](http://upload-images.jianshu.io/upload_images/3373032-9f44b9d0b7329c7a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

3.使用这个地址

![3.png](http://upload-images.jianshu.io/upload_images/3373032-18056b5020add779.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

4.打开调试工具,可以发现Head部分是301,也就是发生了重定向

![4.png](http://upload-images.jianshu.io/upload_images/3373032-8bdef90683b18238.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

5.来看看网页内容,可以看到,我们得到了该目录下可以访问的文件条目

![5.png](http://upload-images.jianshu.io/upload_images/3373032-cb9c9e1934c412ef.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

6.最后,控制台信息

![6.png](http://upload-images.jianshu.io/upload_images/3373032-421376c5ef9f7914.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

OK 就是这样。欢迎收看你的月亮我的心,好男人就是我~ 我们下周同一时间再会~

PS:如果本文有错误的地方请您一定要指出来,我会虚心接受,万分感谢。~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值