使用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,如果有就显示,如果没有就列出该目录下的所有文件及文件夹,并可以进一步访问。

6、形如http://xxx.com/a/b, 则作301重定向到http://xxx.com/a/b/ , 这样可以解决内部资源引用错位的问题。

总体的思路如下:

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("<h1>404 Not Found</h1>");
        };
        //没出错 并且文件存在
        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("<h1>500 Server Error</h1>");

            });
            //读取文件
            stream.pipe(response);
            //response.end();  这个地方有坑,加了会关闭对话,看不到内容了
        };
        //如果路径是目录
        if (!err && stats.isDirectory()) {

            var html = " <head><meta charset = 'utf-8'/></head>";
            //读取该路径下文件
            fs.readdir(filePath, (err, files) => {
                if (err) {
                    console.log("读取路径失败!");
                } else {

                    // files.foreach(function (file) {
                    // //做成一个链接表,方便用户访问
                    // html+=`<div><a href="${file}">${file}</a></div>`;
                    //  });

                    for (var file of files) {
                        if (file === "index.html") {

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

                            break;
                        };
                        html += `<div><a href='${file}'>${file}</a></div>`;
                        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:如果本文有错误的地方请您一定要指出来,我会虚心接受,万分感谢。~~
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容

  • 个人入门学习用笔记、不过多作为参考依据。如有错误欢迎斧正 目录 简书好像不支持锚点、复制搜索(反正也是写给我自己看...
    kirito_song阅读 2,445评论 1 37
  • Node.js是目前非常火热的技术,但是它的诞生经历却很奇特。 众所周知,在Netscape设计出JavaScri...
    w_zhuan阅读 3,607评论 2 41
  • Node.js是目前非常火热的技术,但是它的诞生经历却很奇特。 众所周知,在Netscape设计出JavaScri...
    Myselfyan阅读 4,061评论 2 58
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 文|爱读分享者 自从这部韩剧袭来,颜值担当的宋仲基就拨动了万千追剧少女的心弦,成了诸多“韩粉”最新的“舔屏对象”。...
    爱读分享阅读 509评论 0 0