Node.js一行代码实现静态文件服务器的方法步骤


Posted in Javascript onMay 07, 2019

静态文件服务器实现

nodejs不仅仅可以用来写服务端接口,用来做静态文件服务器替代nginx的功能, 也是分分钟可以搞定的。 话不多说,先上代码:

var server=http.createServer(function (req,res){
 fs.createReadStream(Path.resolve(__dirname,"."+req.url)).pipe(res);
})

在项目根目录建一个hello.html文件测试一下 hello.html内容如下:

<h1>hello,world</h1>

node app.js运行,打开浏览器访问一下: http://localhost/hello.html

Node.js一行代码实现静态文件服务器的方法步骤

我们再回头审视一下代码,的确就只有这么简单,这要归功于node Stream类 pipe方法的强大,fs.createReadStream读取本地文件创建一个可读流(ReadStream类的实例),再使用pipe导流到res响应流,res是一个http.ServerResponse类的实例,是一个可写流,继承自 Stream类

http.ServerResponse类的继承关系如下:

Node.js一行代码实现静态文件服务器的方法步骤

安全性考虑

上述代码实现静态文件服务器后,意味着项目根目录下所有的文件(递归)都可以通过浏览器直接访问和下载了,这样会带来一些安全性的问题,想想看,你的服务器端代码和配置文件都能通过浏览器直接下载了,因此需要在代码里加一些限制,例如只能访问特定的目录下的文件和特定扩展名的文件,这样还不够,参考OWasp Top 10安全风险(第4条-不安全的对象直接引用),攻击者仍然可以通过../../目录回溯的方法访问到其它目录,对于访问路径中包含..的也要全部过滤掉。

实现mine type

mime type是指http 响应头中的content-type字段,它决定了浏览器如何解析文件,是直接当做纯文件显示(text/plain),还是做为html文件渲染(text/html),或者当做二进制文件下载,没有输出正确的mine type,可能导致图片文件无法显示,字体文件无效,视频文件无法播放的问题。要实现起来也十分简单,只需要做一个映射表,不同文件扩展名,在响应头的content-type字段中输出对应的mine type就行了。

完整代码如下:

const http=require("http");
const Path=require("path");
const fs=require("fs");

var server=http.createServer(function (req,res){
 const fileName=Path.resolve(__dirname,"."+req.url);
 const extName=Path.extname(fileName).substr(1);

 if (fs.existsSync(fileName)) { //判断本地文件是否存在
  var mineTypeMap={
   html:'text/html;charset=utf-8',
   htm:'text/html;charset=utf-8',
   xml:"text/xml;charset=utf-8",
   png:"image/png",
   jpg:"image/jpeg",
   jpeg:"image/jpeg",
   gif:"image/gif",
   css:"text/css;charset=utf-8",
   txt:"text/plain;charset=utf-8",
   mp3:"audio/mpeg",
   mp4:"video/mp4",
   ico:"image/x-icon",
   tif:"image/tiff",
   svg:"image/svg+xml",
   zip:"application/zip",
   ttf:"font/ttf",
   woff:"font/woff",
   woff2:"font/woff2",

  }
  if (mineTypeMap[extName]) {
   res.setHeader('Content-Type', mineTypeMap[extName]);
  }
  var stream=fs.createReadStream(fileName);
  stream.pipe(res);
 }

 
})
server.listen(80);

实现gzip

对于文本类型的文件,如html,js,css,采用gzip压缩可以大幅减少传输量,提升服务器传输性能,当然这会损耗一点服务器的cpu性能做为代价,如果客户端浏览器支持gzip压缩,则会在请求头的accept-encoding中携带gzip关键字,用node自带的zlib类就可以实现gzip压缩了,只要在stream.pip实多加一层,先导流到gzip流,再导出到res流,当然,还要在响应头中添加Content-Encoding为gzip,这样浏览器才能正确识别到http body是采用gzip算法压缩的,并进行自动解压缩。

代码如下:

const zlib = require('zlib');

if (req.headers["accept-encoding"].indexOf("gzip")>=0 && (extName=="js" || extName=="css" || extName=="html"))) {
  res.setHeader('Content-Encoding', "gzip");
  const gzip = zlib.createGzip();
  stream.pipe(gzip).pipe(res);
 }

客户端缓存

http协议的缓存协商流程比较长,最终在响应头中生成expire(绝对时间)和cache-control(相对时间)两个用于控制缓存过期时间的参数,浏览器下次请求该文件时,分为以下几种情况:

  • 如果没到过期时间,浏览器不会请求文件直接读缓存
  • 如果已到过期时间,则会在请求头中last-modified字段携带文件的最后修改日期,如果对比时间戳与服务器文件一致,则HTTP 返回 304: Not Modified
  • 如果按下f5刷新,会在请求头中if-modified-since字段中携带缓存的过期时间,如果对比时间戳与服务器文件一致,则HTTP 返回 304: Not Modified
  • ctrl+f5刷新,请求头中携带 cache-control: no-cache,强制禁用缓存。重新下载文件

逻辑分支较多,但都是日期比对,搞清楚缓存协商过程比较容易写出来,有兴趣的同学可以自行实现

高性能静态文件服务器优化

如果要做一个高性能的静态文件服务器仅实现gzip和缓存协商是不够的,涉及到本地文件的频繁读取,高并发下I/O必定成为瓶颈,考虑到服务器上的文件是很少更新的, 可以用Buffer把文件流缓存到内存中,每次请求时先在内存中查找匹配项,如果命中了直接从内存中返回,避免了读取磁盘,gzip也不用压缩了,直接用压缩好的文件流返回,可以成倍的大幅提升性能。当然如果文件太多了,内存也会飙升,需要考虑淘汰算法,只缓存访问次数高的文件,剔除低访问量的文件。

采用fs.watch监控目录文件的变化,如果文件有更新,则删掉缓存。

小结

Node.js 内置的pipe方法可以非常简便的实现将服务器本地文件输出到http 响应流中,gzip压缩也同样可以通过pipe实现,再配合输出mine type 实现的静态服务器已经可以满足一般业务的使用。如果要实现高性能的静态文件服务器,还需要实现客户端缓存、服务端缓存功能(本文提供了思路,按图索骥也非难事)。

最后,推荐一下个人的开源项目, node.js web开发框架,已包含本文静态文件服务器的功能 webcontext: https://github.com/windyfancy/webcontext

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

Javascript 相关文章推荐
html+js实现动态显示本地时间
Sep 21 Javascript
JS冒泡事件的快速解决方法
Dec 16 Javascript
封装了一个支持匿名函数的Javascript事件监听器
Jun 05 Javascript
原生JavaScript+LESS实现瀑布流
Dec 12 Javascript
node.js中的http.get方法使用说明
Dec 14 Javascript
javascript使用appendChild追加节点实例
Jan 12 Javascript
正则表达式优化JSON字符串的技巧
Dec 24 Javascript
vue-quill-editor实现图片上传功能
Aug 08 Javascript
JavaScript实现body内任意节点的自定义属性功能示例
Sep 18 Javascript
React Native中导航组件react-navigation跨tab路由处理详解
Oct 31 Javascript
解决JSON.stringify()自动将中文转译成unicode的问题
Jan 05 Javascript
JavaScript 浏览器对象模型BOM原理与常见用法实例分析
Dec 16 Javascript
微信小程序扫描二维码获取信息实例详解
May 07 #Javascript
Vue数据绑定简析小结
May 07 #Javascript
javascript实现对话框功能警告(alert 消息对话框)确认(confirm 消息对话框)
May 07 #Javascript
详解Vue、element-ui、axios实现省市区三级联动
May 07 #Javascript
webpack结合express实现自动刷新的方法
May 07 #Javascript
记录一次开发微信网页分享的步骤
May 07 #Javascript
Vue 幸运大转盘实现思路详解
May 06 #Javascript
You might like
用PHP编程开发“虚拟域名”系统
2006/10/09 PHP
php抓即时股票信息
2006/10/09 PHP
laravel 操作数据库常用函数的返回值方法
2019/10/11 PHP
静态的动态续篇之来点XML
2006/08/15 Javascript
jquery ajax 同步异步的执行示例代码
2010/06/23 Javascript
javascript 弹出的窗口返回值给父窗口具体实现
2013/11/23 Javascript
js实现随屏幕滚动的带缓冲效果的右下角广告代码
2015/09/04 Javascript
AngularJS使用ngOption实现下拉列表的实例代码
2016/01/23 Javascript
JS实现登录页面记住密码和enter键登录方法推荐
2016/05/10 Javascript
学习Angularjs分页指令
2016/07/01 Javascript
用jmSlip编写移动端顶部日历选择控件
2016/10/24 Javascript
js前端解决跨域问题的8种方案(最新最全)
2016/11/18 Javascript
bootstrap输入框组件使用方法详解
2017/01/19 Javascript
jQuery操作DOM_动力节点Java学院整理
2017/07/04 jQuery
通过nodejs 服务器读取HTML文件渲染到页面的方法
2018/05/17 NodeJs
Vue3 的响应式和以前有什么区别,Proxy 无敌?
2020/05/20 Javascript
解决vue单页面应用进入页面加载所有 js 的问题
2020/08/12 Javascript
vue 判断两个时间插件结束时间必选大于开始时间的代码
2020/11/04 Javascript
linux下安装easy_install的方法
2013/02/10 Python
使用Python来开发微信功能
2018/06/13 Python
django框架基于模板 生成 excel(xls) 文件操作示例
2019/06/19 Python
python查看文件大小和文件夹内容的方法
2019/07/08 Python
在Pycharm中调试Django项目程序的操作方法
2019/07/17 Python
python实现一个点绕另一个点旋转后的坐标
2019/12/04 Python
python连接打印机实现打印文档、图片、pdf文件等功能
2020/02/07 Python
python numpy实现多次循环读取文件 等间隔过滤数据示例
2020/03/14 Python
CSS3 border-radius圆角的实现方法及用法详解
2020/09/14 HTML / CSS
html5 sessionStorage会话存储_动力节点Java学院整理
2017/07/06 HTML / CSS
HTML5的文档结构和新增标签完全解析
2017/04/21 HTML / CSS
基于HTML5 的人脸识别活体认证的实现方法
2016/06/22 HTML / CSS
世界第一冲浪品牌:O’Neill
2016/08/30 全球购物
The Beach People美国:澳洲海滨奢华品牌
2018/07/05 全球购物
物流专业大学生求职信范文
2013/10/28 职场文书
12岁生日演讲稿
2014/05/14 职场文书
2016春节家属慰问信
2015/03/25 职场文书
婚礼答谢词范文
2015/09/29 职场文书