NodeJS缓存机制:畅销货,就多囤一点呗
上一篇文章,我们已经实现了客户端向NodeJS服务器发出请求时,服务器从磁盘读取文件内容后,向客户端返回文件的数据。而对于爱莲(iLinkIT)的1对n的场景,即将文件共享出来之后,让多个用户同时下载,如果每个用户发起请求,我们都重新去磁盘读一下文件,那样岂不是效率低下?本文将重点改进一下效率和体验的问题。
老规矩,先上一个图:

因为对于一个具体的共享任务,文件是同一个的,我们可以只做一次读取文件的操作,把读取进来的文件数据先保存到缓冲区。当有客户端发送请求时,就从缓冲区读取数据响应对应的客户端。
代码如下:
var http = require( 'http' );
var fs = require('fs'); var file_path = "D:\\ilinkit_logo.png" ;
var file_stream ;
var buffer_box = [] ;
var file_length = 0 ; fs.stat( file_path , function ( err , stat ){
if (err) {
if ('ENOENT' == err.code) {
console.log( 'File does not exist...' );
} else {
console.log( 'Read file exception...' );
}
} else {
file_stream = fs.createReadStream( file_path );
file_stream.on( 'data' , function( chunk ){
buffer_box.push( chunk ) ;
file_length += chunk.length ;
} );
file_stream.on( 'end' , function( ){
console.log( "文件读取完毕" );
} );
file_stream.on('error', function(err){
console.log( "文件读取失败!" );
}); var server =http.createServer( function ( request ,response ){
for( var buffer_index = 0 ; buffer_index<buffer_box.length ; buffer_index++ )
{
response.write( buffer_box[buffer_index] );
}
response.end();
} );
server.listen( 8000 );
console.log( 'HTTP服务器启动中,端口:8000.....' ); }//end else,读取文件没有发生错误
});
先解释一下整体的框架调整。从第9行~第40行,都是用fs.stat检查文件的相关信息,如果文件存在不存在,或者其他异常(例如:当前用户无权限读取该文件等。),就什么也不做,退出程序。如果读取文件正确,则创建一个HTTP服务器(第29行~第36行)。
关键代码解析如下:
第6行和第7行,声明保存文件数据的缓冲区的相关变量。
第19行和第20行,将文件的数据保存到缓冲区中。
第30行~第34行,当有客户端有发送请求时,从缓冲区读取数据,通过response对象向客户端传送数据。
验证方式如下:
1. 启动服务器:打开命令行,进入js脚本所在的位置,执行:node f_ilinkit_1.js。
2. 打开浏览器,输入:http://localhost:8000,显示如下:

改进1:
建立了缓冲区,客户端提交请求之后,直接从缓冲区返回数据,貌似整个过程已经很完美了,但是,其实,咱们的程序还存在致命的问题。我们在向客户端响应数据的时候,是非常简单地调用 response.write( buffer_box[buffer_index] ); 来响应,但是,知道HTTP协议的同学都知道,如果用Web服务器的标准来看,客户端和服务器之间的响应,是还需要进行“响应头”的设置的,这样,客户端才知道接收到的数据是什么类型?应该如何处理?
因为我们之前用来测试的文件是D:\ilinkit_logo.png,客户端(浏览器)接收到一个图片的文件之后,就把它显示在浏览器中了,如果我们共享的文件是zip文件,是rar文件,会发生什么呢?所以,服务器在向客户端发送数据时,应该设置“响应头”的内容,让客户端把当前的数据当作一个附件来处理,这也符合我们爱莲(iLinkIT)的业务场景。改进后的代码如下:
var http = require( 'http' );
var fs = require('fs'); var file_path = "D:\\ilinkit_logo.rar" ;
var file_stream ;
var buffer_box = [] ;
var file_length = 0 ; var file_name = file_path.substr( file_path.lastIndexOf('\\')+1 ); fs.stat( file_path , function ( err , stat ){
if (err) {
if ('ENOENT' == err.code) {
console.log( 'File does not exist...' );
} else {
console.log( 'Read file exception...' );
}
} else {
file_stream = fs.createReadStream( file_path );
file_stream.on( 'data' , function( chunk ){
buffer_box.push( chunk ) ;
file_length += chunk.length ;
} );
file_stream.on( 'end' , function( ){
console.log( "文件读取完毕" );
} );
file_stream.on('error', function(err){
console.log( "文件读取失败!" );
}); var server =http.createServer( function ( request ,response ){
response.setHeader( 'Content-Type' , 'application/octet-stream' );
response.setHeader( 'Content-Disposition' , 'attachment; filename=' + encodeURIComponent(file_name) ); for( var buffer_index = 0 ; buffer_index<buffer_box.length ; buffer_index++ )
{
response.write( buffer_box[buffer_index] );
}
response.end();
} );
server.listen( 8000 );
console.log( 'HTTP服务器启动中,端口:8000.....' ); }//end else,读取文件没有发生错误
});
关键的改进点说明如下:
第4行,共享的文件,我们把ilinkit_logo.png修改为ilinkit_logo.rar。当然,在D:下应该放一个ilinkit_logo.rar的文件。
第9行,我们从共享的文件路径中,解析出文件名(例子中就是:ilinkit_logo.rar),用于向客户端响应时,告知当前附件的文件名。
第32行和第33行,在向客户端提供文件数据之前,先设置响应的内容的类型(Content-Type)和内容特点(Content-Disposition),告诉客户端,要将接收到数据当附件处理,文件名为ilinkit_logo.rar。
验证方式如下:
1. 先将ilinkit_logo.png压缩成ilinkit_logo.rar,压缩后的文件依然放到D:下面。
2. 启动服务器:打开命令行,进入js脚本所在的位置,执行:node f_ilinkit_2.js。
3. 打开浏览器,输入:http://localhost:8000,显示如下:

我们服务器里提供的共享文件是ilinkit_logo.rar,所以,用客户端访问,服务器就向客户端响应一个附件文件,其实,上面的代码中,我们如果把ilinkit_logo.rar修改为ilinkit_logo.png,浏览器收到文件数据之后,仍然会把它当“附件”处理,而不会显示在浏览器中,因为我们设置了响应头的内容。
共享文件修改为ilinkit_logo.png的时候,浏览器下载效果如下:

【要点回顾】
今天的解说就到这里,我们一起来回顾一下要点:
1. 改进了响应数据的方式,将要共享的文件先读取到缓冲区,提高响应效率。
2. 通过设置“响应头”,告知客户端要将接收到的数据,当“附件”处理。
感谢诸位捧场,欢迎多提宝贵建议,谢谢^_^~~
-----------------------爱莲(iLinkIT)系列文章------------------------------------------
NodeJS缓存机制:畅销货,就多囤一点呗的更多相关文章
- 利用nodejs模块缓存机制创建“全局变量”
在<深入浅出nodejs>有这样一段(有部分增减): 1.nodejs引入模块分四个步骤 路径分析 文件定位 编译执行 加入内存 2.核心模块部分在node源代码的编译过程中就编译成了二级 ...
- hibernate缓存机制详细分析 复制代码 内部资料 请勿转载 谢谢合作
您可以通过点击 右下角 的按钮 来对文章内容作出评价, 也可以通过左下方的 关注按钮 来关注我的博客的最新动态. 如果文章内容对您有帮助, 不要忘记点击右下角的 推荐按钮 来支持一下哦 如果您对文章内 ...
- Hibernate的一级二级缓存机制配置与测试
特别感谢http://www.cnblogs.com/xiaoluo501395377/p/3377604.html 在本篇随笔里将会分析一下hibernate的缓存机制,包括一级缓存(session ...
- Volley(六 )—— 从源码带看Volley的缓存机制
磁盘缓存DiskBasedCache 如果你还不知道volley有磁盘缓存的话,请看一下我的另一篇博客请注意,Volley已默认使用磁盘缓存 DiskBasedCache内部结构 它由两部分组成,一部 ...
- 页面静态化2 --- 使用PHP缓存机制来完成页面静态化(上)(ob_flush和flush函数区别用法)
我们可以使用PHP自带的缓存机制来完成页面静态化,但在这里,需要说明一点,仅靠PHP缓存机制并不能完美的解决页面静态化,往往需要和其他页面静态技术(通常是伪静态技术)结合使用 例子: 当访问一个页面时 ...
- fackbook的Fresco的Image Pipeline以及自身的缓存机制
fackbook的Fresco的Image Pipeline以及自身的缓存机制 配置之前.首先需要知道两点:一点是Bitmap缓存.一点是如果你仅仅需要一个缓存,那么不调用setSmallImageD ...
- 进击的Hybrid App,量身定做缓存机制
引用张图,简单粗俗的解释下 Native App.Web App 和 Hybrid App Navtie App: 使用平台系统提供的原生语言来编写的 App,如果Android用java,ios用o ...
- hibernate缓存机制详细分析
转自:http://www.cnblogs.com/xiaoluo501395377/p/3377604.html 在本篇随笔里将会分析一下hibernate的缓存机制,包括一级缓存(session级 ...
- Hibernate笔记——缓存机制详细分析
原文:http://www.cnblogs.com/xiaoluo501395377/p/3377604.html ========================================== ...
随机推荐
- 将word转化为swf 进行如同百度文库的般阅读
实现如同百度文库那样类似功能需要进行一系列转化,一般流程想将word转化为pdf格式,再将pdf格式转化为swf格式.在网页上显示其实都是swf格式内容. 首先将word转化为swf,需要调用com组 ...
- 经常使用的android弹出对话框
我们在平时做开发的时候,免不了会用到各种各样的对话框,相信有过其它平台开发经验的朋友都会知道,大部分的平台都仅仅提供了几个最简单的实现,假设我们想实现自己特定需求的对话框,大家可能首先会想到,通过继承 ...
- 深入MySQL源码 学习方法 何登成专家
MYSQL 技术圈 有哪些做得好,又注重分享的公司: Oracle MySQL, MariaDB, Percona,Google, FB, Twitter, Taobao, NetEase… 有哪些值 ...
- eclipse 的小技巧
1. ctrl+o:快速outline 如果想要查看当前类的方法或某个特定方法,但又不想把代码拉上拉下,也不想使用查找功能的话,就用ctrl+o吧.它可以列出当前类中的所有方法及属性,你只需输入你想要 ...
- 琐碎-hadoop2.2.0目录结构
之前了解了一下0.20.2和1.1.0.以后现在主流肯定是2.x吧,包含了之前没有的yarn bin Hadoop最基本的管理脚本和使用脚本,这些脚本是sbin目录下管理脚本的基础实现,用户可以用这些 ...
- 让 BAT 的 Offer 不再难拿
随着各大公司春招的开始,很多小伙伴都行动起来了,我有幸能够加入百度并和大家分享自己的经验心得.由于我面试的都是比较大的公司,所以自然也是做了这方面的准备,因此这篇总结并不一定适合想去创业公司的同学.另 ...
- 刚安装完jdk和eclipse需要配置什么?
还需要配置环境变量,你还要下载 apache-tomcat WEB服务器,也就是说 系统能找到你的 服务器,具体配置:(在windows桌面上右击“我的电脑” —> “属性” —> “高级 ...
- 观察者模式(二)--《Head First DesignPattern》
我们用Java中自带的观察者模式接口来重写前面的例子. 先看一下类图: 这里用到了一个setChanged函数,它用来标记状态已经改变的事实,好让notifyObservers()知道当它调用时就应该 ...
- oracle11g asm standalone 单实例重建
原文地址:oracle11g asm单实例重建has 作者:datapeng 最近到客户那里处理故障,客户说,他们修改了一下hostname,导到has出现了问题,当然,他们的数据库也就无法再启动,把 ...
- 关于js中event的target和currentTarget的区别
今天又遇到这个问题了,总是搞不清楚target和currentTarget的区别,百度搜索的时候看到一遍文章解释得很清楚,特意记录下录,以备不时之需: target与currentTarget的区别? ...