使用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("

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:如果本文有错误的地方请您一定要指出来,我会虚心接受,万分感谢。~~

你可能感兴趣的:(使用Node.js搭建简单静态文件服务器)