node.js笔记之静态文件服务器(一)

时间:2022-04-20 17:58:35

创建一个提供文件查看的静态服务器

效果图如下:

node.js笔记之静态文件服务器(一)

主要思路:

1.定位文件

我们首先需要做的是获取用户输入的url地址,因为通过这个地址我们可以知道用户是想访问哪个文件。
这里将会引入一些新的模块:

var parse = require("url").parse;//解析url
var join = require("path").join;//拼接url

例如我想访问a.js这个文件,我输入的url为:http://localhost:3000/a.js
当这个请求发送给服务器的时候,服务器通过req这个对象可以得到,然后通过parse进行解析;

var url = parse(req.url);
/*通过parse将url进行解析,并返回一个对象
Url {
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: null,
query: null,
pathname: '/a.js'
path: '/a.js',
href: '/a.js'
}
*/

然后寻找服务器中此文件的路径

var root = __dirname;
//C:\Users\liuzj\Desktop\node 获取当前该文件所在路径
var path = join(root, url.pathname);
//通过join进行拼接
//最终可以得到文件所在路径C:\Users\liuzj\Desktop\node\a.js

2.读取文件

当我们找到文件后,就要开始读取文件中的内容了。
这里又需要引入新的模块:

var fs = require('fs');
var through2 = require('through2');//处理stream流

当然这个地方我们不可避免将要学习一下stream的概念
友情链接

为什么会用pipe?
因为文件在读写的时候会出现速度不一致的情况,例如读文件速度快于写文件速度,那么需要暂停一下读文件,等写文件的缓冲区有空余空间了再重新恢复读文件。而pipe正是为我们封装好了这一逻辑,不用我们再手动去暂停或恢复;

下面为不使用pipe,自己手动去暂停和恢复流传输的代码:

        var fsReadStream = fs.createReadStream(path);
fsReadStream.on('data', function (chunk) {
if (!res.write('<a href="/">返回</a><pre>' + chunk + '</pre>')) {
fsReadStream.pause();//暂停
}
});
fsReadStream.on('end', function () {
res.end();
});
fsReadStream.on('error', function () {
res.statusCode = 500;
res.end('Internal Server Error');
})
res.on('drain', function () {
fsReadStream.resume();//恢复
})

这里顺便提一下,下面这样写是不一样的,前面想到于目的地为writeStram2中,后面是分别流入2个管道,目的地不同。

readStream.pipe(writeStram1).pipe(writeStram2);
or
readStream.pipe(writeStram1);
readStream.pipe(writeStram2);

为什么会用through2呢?
因为日常中我们对于数据的处理并不是简单地从一个地方读,再写入一个地方,我们往往会对数据进行修改。例如这个例子中我就对文件中的字符进行了大写转换,然后加了在返回给浏览器的时候加了一个返回;through2则简化了我们的操作;
友情链接

3.处理服务器错误

由于传输的文件是静态的,所以我们可以用fs.stat( )系统调用获取文件的相关信息,之所以要这样处理,是因为考虑到如果用户请求的文件不存在则会出错导致服务器崩溃,所以需要处理服务器错误;

        fs.stat(path, function (err, stat) {
if (err) {
if ('ENOENT' == err.code) {
res.statusCode = 404;
res.end('NOT FOUND');
} else {
res.statusCode = 500;
res.end('Internal Server Error');
}
} else {
//do something
}
})

最后贴一下完整代码:

var http = require("http");
var parse = require("url").parse;
var join = require("path").join;
var fs = require('fs');
var through2 = require('through2');

var root = __dirname;

var server = http.createServer(function (req, res) {
var url = parse(req.url);
var path = join(root, url.pathname);
if (url.pathname == '/') {
var body = [
'<div>',
'<p>文件目录</p>',
'<ul>',
'<li><a href="./a.js">a.js</a></li>',
'<li><a href="./b.js">b.js</a></li>',
'</ul>',
'</div>'
].join("");
res.writeHead(200, {
'Content-Length' : Buffer.byteLength(body),
'Content-Type' : 'text/html;charset=utf-8'
});
res.end(body);
}
fs.stat(path, function (err, stat) {
if (err) {
if ('ENOENT' == err.code) {
res.statusCode = 404;
res.end('NOT FOUND');
} else {
res.statusCode = 500;
res.end('Internal Server Error');
}
} else {
res.writeHead(200, {
'Content-Type' : 'text/html;charset=utf-8'
});
var fsReadStream = fs.createReadStream(path);
var intermediateStream1 = through2(write,end);
var intermediateStream2 = through2(fn,end);
fsReadStream.pipe(intermediateStream1).pipe(intermediateStream2).pipe(res);
fsReadStream.on('error', function () {
res.statusCode = 500;
res.end('Internal Server Error');
})
}
})
}).listen(3000);

function write(data, _, next) {
this.push(data.toString().toUpperCase())
next();
}
function end(done) {
done();
}

function fn(data, _, next) {
this.push('<p><a href="/">返回</a></p><pre>' + data + '</pre>')
next();
}