Node.js开发入门—HTTP文件服务器

2023-11-18

HelloWorld示例只有演示意义,这次我们来搞一个实际的例子:文件服务器。我们使用Node.js创建一个HTTP协议的文件服务器,你可以使用浏览器或其它下载工具到文件服务器上下载文件。

用Node.js实现的HTTP文件服务器,比我在Qt网络编程实战之HTTP服务器视频课程里用Qt实现的版本,复杂度要小太多太多了。

为了读取文件,我们会用到File System模块(名字是”fs”),Stream,我们还要分析URL,区别HTTP方法,还会用到EventEmitter。

文件服务器FileServer的代码

先上代码吧,依然是简单的:

// 引入http模块
var http = require("http"); 
var fs = require("fs");

// 创建server,指定处理客户端请求的函数
http.createServer(
    function(request, response) {
        //判断HTTP方法,只处理GET  
        if(request.method != "GET"){
            response.writeHead(403);
            response.end();
            return null;
        }

        //此处也可使用URL模块来分析URL(https://nodejs.org/api/url.html)
        var sep = request.url.indexOf('?');
        var filePath = sep < 0 ? request.url : request.url.slice(0, sep);
        console.log("GET file: " + filePath);

        //当文件存在时发送数据给客户端,否则404
        var fileStat = fs.stat("."+filePath, 
            function(err, stats){
                if(err) {
                    response.writeHead(404);
                    response.end();
                    return null;
                }
                //TODO:Content-Type应该根据文件类型设置
                response.writeHead(200, {"Content-Type": "text/plain", "Content-Length": stats.size});

                //使用Stream
                var stream = fs.createReadStream("."+filePath);

                stream.on('data',function(chunk){
                    response.write(chunk);
                });

                stream.on('end',function(){
                    response.end();
                });

                stream.on('error',function(){
                    response.end();
                });
            }
        );
    }
).listen(8000); 

console.log("Hello World start listen on port 8000");

最大的变化,就在传递给createServer方法的参数了。

我们根据request.method作了判断,不是GET就返回403。如果是呢,就判断文件是否存在,不存在,返回404,存在就读取数据写给客户端。逻辑就是这么简单。下面我们来介绍用到的新知识。

File System

要使用FileSystem,得用require引入fs模块,就如前面代码里那样。File System的API老长老长了,看这里吧:https://nodejs.org/api/fs.html。我们只说用到的特性。

获取文件状态

在我们的FileServer里,收到和客户端请求时先通过fs.stat()方法获取文件状态。fs.stat()方法原型如下:

fs.stat(path, callback)

第一个参数是文件路径,第二个参数是回调函数。fs.stat()方法是异步的,结果通过回调函数callback返回。callback的原型如下:

function(err, stats)

第一个参数指示是否出现了错误,第二个参数是一个对象,类型是fs.Stats,保存了文件的状态信息,比如大小、创建时间、修改时间等。

FileServer的代码获取到文件状态后,读取大小,调用http.ServerResponse的writeHead方法,设置HTTP状态码为200,还设置了Content-Length头部。代码如下:

response.writeHead(200, {"Content-Type": "text/plain", "Content-Length": stats.size})

ReadStream

接下来呢,我们调用fs.createReadStream创建了一个ReadStream对象。ReadStream是Stream,也是EventEmitter。

fs.createReadStream方法原型如下:

fs.createReadStream(path[, options])

第一个参数是文件路径,第二个参数是可选的JSON对象,用来指定打开文件的一些选项,默认值如下:

{ flags: ‘r’,
encoding: null,
fd: null,
mode: 0666,
autoClose: true
}

autoClose属性默认为true,读完文件或读取出错时,文件会被自动关闭。fd属性可以关联一个已有的文件描述符,这样就会忽略path,根据一个已经打开的文件来创建流。options还可以有start和end项,指定起、止位置,读取文件的特定区域。如果我们要实现断点续传,就需要这个了,用法类似这样:

fs.createReadStream('sample.mp4', {start: 1000, end: 10000});

encoding用来指定文件的编码,这对于文本文件有特殊的意义,目前支持’utf8’、’ascii’和’base64’。

ReadStream读取数据是异步的,一块一块的读,读到一部分就发送一个data事件,数据呢,会传递给与事件关联的listener(实际上是一个回调方法)。在我们的代码里,仅仅是调用response.write把数据写给客户端。注意,可能会多次调用response.write哦。又因为我们设置了Content-Length,所以不会采用chunked编码方式。如果我们不设置Content-Length,那默认会启用chunked方式。

ReadStream读完文件时会发射end事件,出错时会发射error事件,我们监听这两个事件,简单的终止响应。

我们在示例代码中看到了stream.on这种代码,下面来解释吧。

EventEmitter

Node.js基于V8引擎实现的事件驱动IO,是其最大最棒的特色之一。有了事件机制,就可以充分利用异步IO突破单线程编程模型的性能瓶颈,使得用JavaScript作后端开发有了实际意义。

EventEmitter的基本用法

events.EventEmitter是一个简单的事件发射器的实现,具有addListener、on、once、removeListener、emit等方法,开发者可以很方便的调用这些API监听某个事件或者发射某个事件。

我们在示例中用到的fs.ReadStream就是一个EventEmitter,它实现了stream.Readable接口,而stream.Readable具有data、error、end、close、readable等事件。

通常我们使用EventEmitter的on或addListener来监听一个事件,这个时间可能会多次触发,每次触发,我们提供的回调方法都会被调用。我们示例中的代码就是这样:

        stream.on('data',function(chunk){
            response.write(chunk);
        });

Node.js的事件机制,会给某个事件关联一个回调方法列表,这样多个关注者就可以监听同一个事件。每个事件发射时,可能会带有数据和状态,这些数据是通过回调方法的参数传递出来的。那某一个特定的事件,它对应的回调方法的参数是什么样子的,则由事件定义的那个类(实例)来决定。EventEmitter的emit方法原型如下:

emitter.emit(event[, arg1][, arg2][, ...])

这个原型说明一个事件的回调方法可以有一个或多个参数,也可以没有参数。要想知道某个事件的回调方法是否有参数、每个参数的含义,只好去找相关的API文档。stream.Readable的data事件的参数是chunk,Buffer类型,代表读到的数据。

如果我们只想监听某个事件一次,则可以调用EventEmitter的once方法。要想移除一个事件监听器,可以调用removeListener,想移除所有,则可以调用removeAllListener。

自定义事件

Node.js的很多模块都继承自Event模块。我们自己也可以通过继承EventEmitter来实现自己的对象,添加自己的自定义事件。

这里有个简单的例子:

var util=require("util");
var events = require("events");
function Ticker() {
    var self = this;
    events.EventEmitter.call(this);
    setInterval(function(){
        self.emit("tick")
        },
        1000
    );
}
util.inherits(Ticker, events.EventEmitter);

var ticker = new Ticker();
ticker.on("tick", function() {
  console.log("tick event");
});

在这个简单的例子里,我们定义了Ticker对象,通过全局方法setInterval开启了一个定时器,每隔1000毫秒发射一个名为“tick”的事件。

Node.js的工具模块封装了继承的方法,我们调用它来的inherits方法来完成Ticker对events.EventEmitter的继承。

自定义事件的使用方法,和Node.js内置模块提供的事件的用法完全一样。


其它文章:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Node.js开发入门—HTTP文件服务器 的相关文章

随机推荐

  • Linux入门篇-01 台式机安装Linux系统

    材料 台式机 U盘 内存大于8G CentOs7 下载地址 点击跳转 步骤一 U盘启动电脑 启动成功画面 选择第一个 按E 或者 Tab键 进行编辑 vmlinuz initrd initrd img inst stage2 hd LABE
  • 传感器响应时间与滤波器截止频率的关系

    1 传感器响应时间与滤波器截止频率的关系 详细看 https blog csdn net Old Street article details 108087826 2 滤波器中关于截止频率的理解 http www 360doc com co
  • 孤立森林算法(Isolation Forest Algorithm)Python实现

    孤立森林算法 Isolation Forest Algorithm Python实现 孤立森林算法是一种用于检测异常值的无监督机器学习算法 它通过构建一棵随机划分的二叉树来判断数据点的异常程度 以此来识别异常值 本文将介绍孤立森林算法的原理
  • ubuntu20.04 安装 Sophus库 slambook2

    ubuntu20 04 安装 Sophus库 slambook2 slambook2 实践部分 需要我们自行安装 sophus 库 但是相关的依赖很多 而且sophus分为模板类和非模板类两种 安装起来还是比较繁琐的 slambook2中采
  • QT在子线程发送信号给主线程,主线程信号槽函数没有反应的解决办法

    在编写线程时遇到了一个问题 即子线程发送信号给主线程 主线程信号槽函数没有反应 这个问题卡了半天 最终找到解决办法 自己记录一下 问题 在子线程的run函数发送了一个信号 在主函数中定义了一个信号槽函数用来响应这个信号 但是槽函数不执行 修
  • 我要写一篇关于酒店管理系统论文,请说一说酒店管理系统的研究背景

    酒店管理系统的研究背景是指针对旅游业和酒店业的扩大发展 以及客户服务需求的日益增加 许多酒店已经使用酒店管理系统来提高服务质量和管理效率 酒店管理系统的研究着重于通过使用软件和信息技术来有效地管理酒店的各项运营活动 从而提升酒店的服务质量
  • 机器学习之多元线性回归

    1 多元线性回归模型 定义 具有多个特征的线性回归模型 多元线性回归模型 见下图 多元线性回归总结 参数 模型 损失函数 梯度下降算法 见下图 注意 梯度下降算法每次都是同时更新wj和b 2 多维特征 多维特征 x1 x2 xn 其中xj表
  • 【Linux学习笔记】7. Linux文件IO详解(附代码实例)

    Linux文件I O 前置知识 Linux文件I O分为系统IO和标准IO 常用于系统编程 系统I O通过文件描述符 fd 来操作文件 标准I O通过文件流 FILE 来操作文件 Linux下可以使用man命令来查看使用手册 学习和使用这些
  • 数据备份技术知识梳理(建议收藏)

    所谓数据保护技术是指对当前时间点上的数据进行备份 如果说原始数据被误删除了 可以通过备份数据找回或恢复数据 从底层来分 数据保护可以分为文件级保护和块级保护 文件级备份 文件级备份 将磁盘上所有文件通过调用文件系统接口备份到另一个介质上 也
  • 11-7 读写指定大小的字节

    1 字节 一个字节 8 位 例如在 ASCII 码表中 0000 1010 表示换行 若从十六进制角度看 则结果为 0a CLion debug 便是以十六进制查看的字节 2 读字节 fread 函数用于指定字节大小的读取 该函数可读取二进
  • 重启大法好

    在做springMVC服务器的时候 出现解析不了URL 即dispatch映射不了action的时候 1 检查springname servlet xml 2 检查web xml 3 检查注解是否错误 4 重启eclipse 5 重启电脑
  • Unity3D射线检测

    射线检测主要用于像子弹是否打中物体 捡取物品等情况 本来面向百度想找例子看看 不过没找到合适的 还是自己总结尝试吧 以下测试Unity3D版本 2017 4 2f2 射线的检测步骤如下 1 Ray 这个类为了产生一个射线 如果我们想要场景中
  • Acwing 906. 区间分组

    1 将所有区间按照左端点从小到大排序 2 从前往后处理每个区间 判断能否将其放到某个现有的组中 L i gt Max r 1 如果不存在这样的组 则开新组 然后将其放进去 2 如果存在这样的组 将其放进去 并更新当前组的Max r incl
  • cocoscreator 3.x 获取像素颜色

    const pos v2 世界坐标 const color as camera rt targetTexture readPixels pos v2 x pos v2 y 1 1 获得颜色 cc color color as 0 color
  • BeautifulSoup4(bs4)

    BeautifulSoup4是一个高效的网页解析库 可以从HTML或XML文件中提取数据 支持不同的解析器 比如 对HTML解析 对XML解析 对HTML5解析 就是一个非常强大的工具 爬虫利器 一个灵感又方便的网页解析库 处理高效 支持多
  • java 枚举应用_Java枚举应用

    JDK5 0开始引进了java Enum枚举类型 它可作为我们在编写代码时的一个技巧 有时恰恰因为它 我们能够 优雅而干净 地解决问题 在使用枚举类型时 我们所编写的枚举类都是隐式地继承于java lang Enum类 枚举类的每个成员都被
  • 报错:ImportError: XXXX.so:invalid ELF header

    运行程序遇到如下报错 原因是该路径下的 so文件与运行程序的环境不匹配 比如我在mac电脑上编译生成的 so文件 直接放到linux服务器上跑了 自然会有错误 解决的方法是在Linux环境中重新编译生成新的 so文件
  • C语言非常道 6.7

    使用结构类型 计算结构类型的大小 在上面这个程序中包含一个输入输出的头文件即可 这个位置又发现一个问题 在定义结构体的时候 数组的大小是一定要标明的 否则在计算结构体大小的时候 编译无法通过
  • C/C++ 安全编码 —— 指针与内存

    1 仿踩内存 if buf len 1 0x5A return
  • Node.js开发入门—HTTP文件服务器

    HelloWorld示例只有演示意义 这次我们来搞一个实际的例子 文件服务器 我们使用Node js创建一个HTTP协议的文件服务器 你可以使用浏览器或其它下载工具到文件服务器上下载文件 用Node js实现的HTTP文件服务器 比我在Qt