Node.js创建HTTP文件服务器的使用示例
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老长老长了,看这里吧:。我们只说用到的特性。
获取文件状态
在我们的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头部。代码如下:
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中的数组合并方法和对象合并方法
下一篇: 才没被吓昏过去
推荐阅读
-
Node.js 使用axios读写influxDB的方法示例
-
PHP 使用header函数设置HTTP头的示例解析 表头
-
Node.js中流(stream)的使用方法示例
-
使用Python创建简单的HTTP服务器的方法步骤
-
Node.js中的http请求客户端示例(request client)
-
使用Node.js实现RESTful API的示例
-
C#验证码的创建与使用示例
-
node.js实现http服务器与浏览器之间的内容缓存操作示例
-
node.js中process进程的概念和child_process子进程模块的使用方法示例
-
node.js使用 http-proxy 创建代理服务器操作示例