通过上一篇文章“NodeJS服务器:一行代码 = 一个的HTTP服务器”,我们已经开启了NodeJS之旅,开发了一个监听在8000端口的HTTP服务器,虽然功能很简单,但是,已经让我们感受到用NodeJS开发服务器是一件简单、愉快的事情。现在,我们按着既定的目标----将电脑里的文件共享给手机,继续前进。

老规矩,先上一个图:

回到我们的项目目标,要实现的功能是:当有客户端向NodeJS服务器发送请求的时候,就读取电脑D:\下面的

ilinkit_logo.png的图片文件作为响应,反馈给客户端,代码如下:

 var http = require( 'http' );
var fs = require('fs'); var file_path = "D:\\ilinkit_logo.png" ;
var file_stream; var server =http.createServer( function ( request ,response ){
file_stream = fs.createReadStream( file_path );
file_stream.on( 'data' , function( chunk ){
response.write( chunk );
} );
file_stream.on( 'end' , function( ){
response.end( "" );
console.log( "文件读取完毕" );
} );
file_stream.on('error', function(err){
response.end( "文件读取失败!" );
});
} );
server.listen( 8000 );
console.log( 'HTTP服务器启动中,端口:8000.....' );

这个代码也比较简单,下面对关键的代码行说明如下:

第2行:加载fs模块,因为我们要用它来读取电脑中的本地文件。

第8行:当有客户端有发送请求时,用fs读取文件到文件流之中。

文件流的概念,和Java、C++中文件流的概念类似,相当于建立了一个管道,这个我们待会儿在优化版本中会再次体会到。

第9行、第12行、第16行:分别实现在文件读取过程中,有数据读取到时(data)、数据读取结束(end)和读取数据发生错误时(error)的响应函数。

第10行:将从文件读取到数据,直接通过response响应给客户端。

验证方式如下:

1. 启动服务器:打开命令行,进入js脚本所在的位置,执行:node e_ilinkit_1.js,如下所示:

2. 打开浏览器,输入:http://localhost:8000,显示如下:

当完成对客户端请求的响应之后,服务器端输出日志:文件读取完毕,如下所示:

改进1:

前面我们提到,读取文件时,使用的是用fs.createReadStream返回文件流,而给客户端响应数据的response对象,也像一个管道,

也像一个“流”,这么看,向客户端响应文件时,是不是将两个管道对接起来就OK了呢? NodeJS 还真有这样的机制。

下面我们来看一下改进之后的代码:

 var http = require( 'http' );
var fs = require('fs'); var file_path = "D:\\ilinkit_logo.png" ;
var file_stream; var server =http.createServer( function ( request ,response ){
file_stream = fs.createReadStream( file_path );
file_stream.pipe( response );
} );
server.listen( 8000 );
console.log( 'HTTP服务器启动中,端口:8000.....' );

重点看第9行:通过fs.createReadStream将文件数据流之后,直接调用pipe函数,file_stream.pipe( response ); 将文件中的数据响应给客户端。

相当于,你去中药房取药,之前的方式是:你到药房的窗口,把药方给里面的工作人员,工作人员依据药方的清单,到货架上一样一样的取药。全部取完之后,拿个袋子一装,然后跟你说:“都在这里了,您核对一下。”

改进之后呢?整个过程变得自动化,不需要工作人员一样一样的取药。你到窗口在按键上输入你的药单编号,然后里面的这个自动化程序如何做呢?用一个“管道”将一个“拣药”的窗口和你取药的窗口,用一个管子(pipe)一连接,自动化程序就不管这件事情了。“拣药程序”按药单拣了药之后,直接通过之前建立的“管道”将药送到你的面前。是不是大大简化了整个过程?

当然,这里举这个改进例子仅仅为了让大伙感受一下NodeJS处理‘流数据’的精巧之处,回到我们的iLinkIT的场景,我们后面会采用其他的方式。

改进2

回到开头小明的例子,张大爷走过来逗小明说:“我不要巧克力,我想要一个果冻。”小明到屋里找了一下,发现没有果冻,小明该怎么办?

同样,异常的处理也是NodeJS的重要特性,前文我们讲过,NodeJS是单进程、单线程的,所以,对于异常的处理需要十分谨慎。读取文件的时候,也有可能出现异常,比如:被读取的文件不存在。

现在,我们就增加异常处理机制,改进后的代码如下:

 var http = require( 'http' );
var fs = require('fs'); var file_path = "D:\\ilinkit_logo.png" ;
var file_stream ; var server =http.createServer( function ( request ,response ){
fs.stat( file_path , function ( err , stat ){
if (err) {
if ('ENOENT' == err.code) {
console.log( 'File does not exist...' );
response.end( 'File does not exist...' );
} else {
console.log( 'Read file exception...' );
response.end( 'Read file exception...' );
}
} else {
file_stream = fs.createReadStream( file_path );
file_stream.on( 'data' , function( chunk ){
response.write( chunk );
} );
file_stream.on( 'end' , function( ){
response.end( "" );
console.log( "文件读取完毕" );
} );
file_stream.on('error', function(err){
response.end( "文件读取失败!" );
});
}//end else,读取文件没有发生错误
});
} );
server.listen( 8000 );
console.log( 'HTTP服务器启动中,端口:8000.....' );

我们重点分析改进的部分:

第8行代码,通过fs.stat可以读取指定文件相关的信息,如果发生异常,则调用异常处理程序,第9行开始的代码;如果没有异常,则读取文件,用文件的内容响应客户端,从第17行开始。从这里可以看出NodeJS中异常处理的一个特点:通常会传入一个包含error对象的回调函数(这里声明为err),异常发生时,通过error对象可以获得异常相关的信息。

第10行到第13行,如果错误的类型是文件不存在,则告知客户端,文件不存在,并在服务端输出日志。

第13行到第16行,如果是其他的异常,则输出另外的提示内容。

第17行到第29行,代码逻辑,和前面的一样,就不再赘述。

验证方式:

1. 启动服务器:打开命令行,进入js脚本所在的位置,执行:node e_ilinkit_3.js

2. 打开浏览器,输入:http://localhost:8000,显示如下:

3. 现在,我们暂时将文件D:\ilinkit_logo.png删除,或者重命名。

打开浏览器,输入:http://localhost:8000,显示如下:

客户端得到的消息是:File does not exist....

【要点回顾】

今天的解说就到这里,我们一起来回顾一下要点:

1. 用fs模块来实现文件的读取。

2. 展示了fs的pipe机制。

3. 结合fs.stat展示了NodeJS异常处理的基本特征。

正如本系列文章所说,这一系列文章的目的是通过一个具体业务场景文件共享传送(iLinkIT),来描述NodeJS的特征,目的是学习。所以,采用一步一步、循序渐进地方式,以方便读者的理解,所以,整个系列文章也有如下特点:

1. 很多知识点没有深入展开讲

比如:我们讲了文件如何读取,但是没有讲文件如何写入。因为我们以一个具体的场景为主线,iLinkIT这个场景不涉及的,用不到的,可能就不会去展开讲。这一系列文章的定位是入门,展现NodeJS的精彩特性。如果想更深入地理解技术细节,在真正的项目中,建议读者参阅官方的社区网站,或更权威的技术指导书。

2. 过程中的思考不一定全面

我们在每一篇文章中,就聚焦几个点,某些地方可能就会简化处理。比如:这篇文章我们讲了如何读取文件,但是,为了简化框架,我们把要读取的文件路径固定写成D:\ilinkit_logo.png,其实,NodeJS也是支持命令传入参数的,但是,如果一下子就和盘托出,面面俱到,就不利于读者理解。

当然啦,正如开篇所述,大家如果有什么好的建议,非常欢迎留言交流,不胜感激^_^~~

-----------------------爱莲(iLinkIT)系列文章------------------------------------------

缘起爱莲:我要的,现在就要!

爱莲(iLinkIT)的架构与原理

遇见NodeJS:JavaScript的贵人

NodeJS服务器:一行代码 = 一个的HTTP服务器

NodeJS文件读取:感恩常在--抓把糖果,愉悦客人

NodeJS缓存机制:畅销货,就多囤一点呗

NodeJS安全设计:好吃的草莓味糖果,只给好朋友小红

NodeJS服务器退出:完成任务,优雅退出

NodeJS文件读取:感恩常在--抓把糖果,愉悦客人的更多相关文章

  1. nodejs 文件读取一行

    作者QQ:1095737364    QQ群:123300273     欢迎加入!   废话没有,直接上代码: app.get('/company', function (req, res, nex ...

  2. 1-2 nodejs小节 文件读取

    1.表达式 在命令行输入  node回车后,可以在后边输入相应的表达式,进行运算操作   2.阻塞文件读取 var data=fs.readFileSync('input.txt', 'utf-8') ...

  3. 用nodejs实现读取文件操作

    //如果不是全局就得引入fs成员 const fs = require("fs"); //fs 核心模块中提供了一个 fs.readFile方法,来读取指定目录下的文件 //fs. ...

  4. H5学习系列之文件读取API--本文转自http://blog.csdn.net/jackfrued/article/details/8967667

    HTML5定义了FileReader作为文件API的重要成员用于读取文件,根据W3C的定义,FileReader接口提供了读取文件的方法和包含读取结果的事件模型. FileReader的使用方式非常简 ...

  5. Linux学习笔记之文件读取过程

    0x00 概述 对于Linux系统来说,一切的数据都起源于磁盘中存储的文件.Linux文件系统的结构及其在磁盘中是如何存储的?操作系统是怎样找到这些文件进行读取的?这一章主要围绕这几个问题进行介绍(以 ...

  6. nodejs 文件操作

    前言: nodejs 自带的文件操作的模块  fs 就是对文件的增删查改: 就像我们用的服务器,我们没有办法在运行的文件上进行一直的修改,因为他不向浏览器,刷新后我们的文件会自己修改: 如果想要更改我 ...

  7. xctf-i-got-id-200(perl网页文件+ARGV上传造成任意文件读取)

    打开url发现有三个链接,点进去都是.pl文件,且只有files可以上传文件. .pl文件都是用perl编写的网页文件 这里上传了又将文件的内容全部打印出来,那么猜想后台应该用了param()函数. ...

  8. Mysql溯源-任意文件读取👻

    Mysql溯源-任意文件读取 前言 读了<MySQL蜜罐获取攻击者微信ID>的文章,文中说明了通过mysql蜜罐读取攻击者微信ID的过程,抱着学习的态度尝试了一下 原理 mysql中有一个 ...

  9. java中的文件读取和文件写出:如何从一个文件中获取内容以及如何向一个文件中写入内容

    import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.Fi ...

随机推荐

  1. Binder机制1---Binder原理介绍

    1.Binder通信机制介绍 这篇文章会先对照Binder机制与Linux的通信机制的区别,了解为什么Android会另起炉灶,採用Binder.接着,会依据Binder的机制,去理解什么是Servi ...

  2. C/C++产生随机数

    <一> C/C++如何产生随机数:这里要用到的是rand()函数, srand()函数,C语言/C++里没有自带的random(int number)函数. (1)  假设你仅仅要产生随机 ...

  3. Keeplived 配制图解

    http://blog.csdn.net/tantexian/article/details/50056229

  4. 如何优雅的实现界面跳转 之 统跳协议 - DarwinNativeRouter

    PS 感谢大家的关注,由于我本想开源4个库,除了router, 另外三个分别是native dispatcher, web dispatcher 和 react dispatcher , 所以rout ...

  5. How to update FVDI Commander driver to latest V2015.6.2

    As FVDI Commander products are upgraded to new versions, I often receive emails from customers askin ...

  6. Android(java)学习笔记76:多线程-定时器概述和使用

    定时器: 定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行. 在Java中,可以通过Timer和TimerTask类来实现定义调度的功能 Timer public Tim ...

  7. C#中ROUND函数的问题 解决

    ROUND()是C#中math的一个成员函数.System.Math.Round(),这个函数有四种用法,最长用的是对小数点位数的舍入.但这和现实生活中的“四舍五入”有一定区别,也有别JAVA中Mat ...

  8. coherence配置说明

    经过上篇 coherence初识 ,最近算是和coherence杠上了,针对coherence3.5.3这个版本,把学到的东西整理下 1. 这个jar包有点大,4M多,首先打开coherence.ja ...

  9. vb.net Linq 筛选(像 select distinct) DateTable 日期数据中的年份

    Private Sub initDDLByYear(ByVal dt As DataTable) ddlByYear.Items.Clear() ddlByYear.Items.Add(") ...

  10. mysql 5.7 root password 过期

    重新修改root密码 SET PASSWORD FOR 'root'@'localhost' = PASSWORD('newpass'); ALTER USER 'root'@localhost' P ...