Node.js创建HTTP文件服务器的使用示例


Posted in Javascript onMay 11, 2018

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

为了读取文件,我们会用到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内置模块提供的事件的用法完全一样。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript innerHTML、outerHTML、innerText、outerText的区别
Nov 24 Javascript
用Javascript 获取页面元素的位置的代码
Sep 25 Javascript
Jquery实战_读书笔记1—选择jQuery
Jan 22 Javascript
jQuery弹出(alert)select选择的值
Apr 21 Javascript
jQuery实现手机号码输入提示功能实例
Apr 30 Javascript
JavaScript中用let语句声明作用域的用法讲解
May 20 Javascript
老生常谈 关于JavaScript的类的继承
Jun 24 Javascript
jQuery之简单的表单验证实例
Jul 07 Javascript
微信小程序 动态传参实例详解
Apr 27 Javascript
JavaScript中undefined和null的区别
May 03 Javascript
浅谈vue项目重构技术要点和总结
Jan 23 Javascript
JS实现图片切换特效
Dec 23 Javascript
Node.js引入UIBootstrap的方法示例
May 11 #Javascript
Node.js使用Angular简单示例
May 11 #Javascript
Node.js 使用AngularJS的方法示例
May 11 #Javascript
Angular使用动态加载组件方法实现Dialog的示例
May 11 #Javascript
详解JavaScript中的数组合并方法和对象合并方法
May 11 #Javascript
Node.js使用cookie保持登录的方法
May 11 #Javascript
实例解析Vue.js下载方式及基本概念
May 11 #Javascript
You might like
UTF8编码内的繁简转换的PHP类
2009/07/09 PHP
php线性表顺序存储实现代码(增删查改)
2012/02/16 PHP
php下Memcached入门实例解析
2015/01/05 PHP
php实现等比例不失真缩放上传图片的方法
2016/11/14 PHP
Mootools 1.2教程 设置和获取样式表属性
2009/09/15 Javascript
JS 树形递归实例代码
2010/05/18 Javascript
菜鸟javascript基础资料整理2
2010/12/06 Javascript
基于Jquery的动态创建DOM元素的代码
2010/12/28 Javascript
jQuery寻找n以内完全数的方法
2015/06/24 Javascript
jQuery实现右下角可缩放大小的层完整实例
2016/06/20 Javascript
ion content 滚动到底部会遮住一部分视图的快速解决方法
2016/09/06 Javascript
浅谈JavaScript find 方法不支持IE的问题
2017/09/28 Javascript
实例讲解javascript实现异步图片上传方法
2017/12/05 Javascript
微信小程序自定义toast的实现代码
2018/11/16 Javascript
微信小程序之swiper滑动面板用法示例
2018/12/04 Javascript
node.js的http.createServer过程深入解析
2019/06/06 Javascript
JavaScript工具库之Lodash详解
2019/06/15 Javascript
vue引入微信sdk 实现分享朋友圈获取地理位置功能
2019/07/04 Javascript
解决layer.prompt无效的问题
2019/09/24 Javascript
在vant 中使用cell组件 定义图标该图片和位置操作
2020/11/02 Javascript
Nuxt pages下不同的页面对应layout下的页面布局操作
2020/11/05 Javascript
vue router返回到指定的路由的场景分析
2020/11/10 Javascript
关于Js中new操作符的作用详解
2021/02/21 Javascript
[01:02]2014 DOTA2国际邀请赛中国区预选赛 现场抢先看
2014/05/22 DOTA
python中sys.argv参数用法实例分析
2015/05/20 Python
python图像处理之镜像实现方法
2015/05/30 Python
快速解决安装python没有scripts文件夹的问题
2018/04/03 Python
django用户登录验证的完整示例代码
2019/07/21 Python
Python中如何引入第三方模块
2020/05/27 Python
使用python实现下载我们想听的歌曲,速度超快
2020/07/09 Python
前端H5 Video常见使用场景简介
2020/08/21 HTML / CSS
施华洛世奇中国官网:SWAROVSKI中国
2020/06/16 全球购物
机械设计制造专业个人求职信
2013/09/25 职场文书
行政处罚告知书
2015/07/01 职场文书
oracle通过存储过程上传list保存功能
2021/05/12 Oracle
Vue elementUI表单嵌套表格并对每行进行校验详解
2022/02/18 Vue.js