读<了不起的Node.js>-06.命令行工具(CLI)以及FS API首个Node应用

简介

  • nodejs中重要的API:处理进程(stdio)的stdin以及stdout相关的API还有文件系统FS的相关api
  • 之前介绍过 node通过使用回调和事件机制来实现并发,现在这些api 会首次接触到基于非阻塞事件的io编程中的流控制
  • 搭建一个简单的命令行文件浏览器,功能是允许用户读取和创建文件

需求

-定义需求

  • 程序需要在命令行运行,意味着程序得通过node命令执行,或者直接执行,然后通过终端提供交互给用户进行输入和输出

  • 启动后显示当前目录下列表

  • 选择文件时,显示文件的内容

  • 选择目录时,显示目录下信息

  • 运行结束后退出

  • 根据需求我们需要做到以下几点

    • 创建模块
    • 决定采用同步的fs 还是异步的fs
    • 理解什么是流 stream
    • 实现输入输出
    • 重构
    • 使用fs进行文件交互
    • 完成

编写首个 node程序

创建模块

  • 先创建文件夹 file-xeplorer
  • 在内部创建 package.json

/这里版本号遵循semver 的版本控制标准/

{
  "name": "file-explorer",
  
  "version": "0.0.1",
  "description": "一个命令行文件资源管理器",
  "dependencies": {}
}
  • 通过命令行输入 npm install 来验证package.json是否有效
  • 接着创建index.js

同步还是异步

  • 我们从生命依赖关系开始,由于stdio Api 是一个全局的process对象的一部分,所以我们这里为一个依赖就是fs
/*
* 模块依赖
* */
const fs = require('fs');
//同步版本
 console.log(fs.readdirSync('.'));

[图片上传失败...(image-3124cc-1533372113516)]

  • index.js
/*
* 模块依赖
* */
const fs = require('fs');

fs.readdir(__dirname, (err, files) =>{
    console.log(files);
});

什么是流 stream

console.log('hello world');
console.log('-----------------');//换行
process.stdout.write('hello world');
process.stdout.write('------------');//不换行
  • process全局对象包含了三个流对象

    • stdin : 标准输入
    • stdout :标准输出
    • stderr : 标准错误
      [图片上传失败...(image-dca6c7-1533372113516)]
  • 第一个stdin 是一个可读流,而stdout和stderr都是可写流

  • stdin流默认的状态是暂停的,通常,执行一个程序,程序会做一些处理,然后退出, 在这里程序需要纸质处在运行状态来接收用户输入的数据

  • 当恢复那个流的时候,node会观察对应的文件描述(unix状态下为0),随后宝石时间循环的运行,同时保持程序不退出,等待事件的触发,除非有io等待,否则nodejs总是会自动退出

输入和输出

  • 这里我们写出第一部分,列出当前目标路下的问文件 然后等待用户输入
/*
* 模块依赖
* */
const fs = require('fs');

fs.readdir(process.cwd(), function (err, files) {
    console.log('');

    if (!files.length) {
        return console.log('   \033[31m 没有文件显示 !\033[39m\n');
    }
    
    console.log('    选择您想要查看的文件或目录\n');

    function file(i) {
        let filename = files[i];

        fs.stat(__dirname + '/' + filename, function (err, stat) {
            if (stat.isDirectory()) {
                console.log('    ' + i + ' \033[36m' + filename + '/\033[39m');
            } else {
                console.log('    ' + i + ' \033[36m' + filename + '\033[39m');
            }
            i++;
            if (i == files.length) {
                console.log('');
                process.stdout.write('    \033[33m输入你的选择: \033[39m');
                process.stdin.resume();
                process.stdin.serEncoding('utf8');
            } else {
                file(i)
            }
        });
    }
    file(0);
});

  • 下面我们来分析上面的代码
  • 为了输出更加友好我们首先输出一个空行:console.log('')
  • 如果files数组为空,告知用户当前目录没有文件 文本周围的\033[31m\033[39m为了让文本呈现为红色,例子中得\n也是为了输出友好console.log(' \033[31m 没有文件显示 !\033[39m\n')
  • 在输出查看语句后
  • 定义了一个函数,数组中每个元素都会执行这个额函数,这里也出现贯穿始终的异步流程控制模式,出阿航执行,
  • function file(i){....}
  • 然后,现货区文件名,在查看文件名对应的路径情况,fs.stat会个提出文件或者目录的元数据let filename = files[i];fs.stat(__dirname + '/' + filename, function (err, stat)..
  • 回调函数中,同时还给出了错误对象和一个stat对象,背离中使用到的stat对象上的方法是isDirectory
  • 如果路径所代表的是目录,我们就用有别于文件的颜色表示出来
  • 下面就是流控制中得核心部分了
    -计数器不断递增,同时检查是否还有为处理的文件
  • 如果所有文件都处理完了 此时提示用户进行选择,注意
  • 这里用的是process.stdout.weite而不是cl,这样就无需换行可以直接在提示语后面输入
  • 这里process.stdin.resume()就是等待用户输入
  • 后面是设置流编码为utf8
  • 如果还有未处理的文件则用递归调用函数进行处理
  • 直到列出所有文件,用户输入完毕,紧接着进行下一步串行处理
  • 重要模式: 串行处理

重构

  • 要做重构,我们从创建快捷变量开始
  • [图片上传失败...(image-6e8c61-1533372113516)]
  • 由于我们书写的代码是异步,会有问题,随着韩数量的增长,过多的函数嵌套会让程序的可读性变差
  • 为此我们可以为每一个异步操作预先定一个一个函数
  • 抽取函数
  • [图片上传失败...(image-85c6-1533372113516)]
  • [图片上传失败...(image-582698-1533372113516)]
  • 读取用户输入后,接下来要做的就是根据用户输入做出相应处理,用户需要选择要读取的文件,所以代码层,我们设置了stdin的编码后,开始监听器data时间:
function read() {
        console.log('');
        stdout.write('    \033[33m输入你的选择: \033[39m');
        stdin.resume();
        stdin.setEncoding('utf8');
        stdin.on('data', option);
    }

    function option(data) {
        if (!files[Number(data)]) {
            stdout.write('    \031[33m输入你的选择: \033[39m')
        } else {
            stdin.pause();
        }
    }
  • 这里我们检查用户的输入是否匹配files数组的下表,files数组是fs.readdir回调函数中的一部分,上述代码中,我们将utf-8编码字符串转换为了Number类型来方便检查
  • 现在我们已经能定位文件了那就开始读取他
    function option(data) {
        const filename = files[Number(data)];
        if (!filename) {
            stdout.write('    \033[33m输入你的选择: \033[39m')
        } else {
            stdin.pause();
            fs.readFile(__dirname + '/' + filename, 'utf8', function (err, data) {
                console.log('');
                console.log('\033[90m'+data.replace(/(.*)/g, '    $1')+'\033[39m');
            });
        }
    }

  • 这样我们已经可以读取一个文件了
  • 但是读取文件夹的时候就会出错,在这种情况下,我们就将其目录下的文件列表显示出来,
  • 为了避免再次执行fs.stat 我们在file函数中,将stat对象保存下来

至此我们就完成了首个查看文件和文件夹内容的程序,虽然很简陋

/*
* 模块依赖
* */
const fs = require('fs'), stdin = process.stdin, stdout = process.stdout;


fs.readdir(process.cwd(), function (err, files) {
    console.log('');

    if (!files.length) {
        return console.log('   \033[31m 没有文件显示 !\033[39m\n');
    }

    console.log('    选择您想要查看的文件或目录\n');
    let stats = [];

    function file(i) {
        let filename = files[i];

        fs.stat(__dirname + '/' + filename, function (err, stat) {
            stats[i] = stat;
            if (stat.isDirectory()) {
                console.log('    ' + i + ' \033[36m' + filename + '/\033[39m');
            } else {
                console.log('    ' + i + ' \033[36m' + filename + '\033[39m');
            }

            if (++i === files.length) {
                read();
            } else {
                file(i)
            }
        });
    }

    function read() {
        console.log('');
        stdout.write('    \033[33m输入你的选择: \033[39m');
        stdin.resume();
        // stdin.setEncoding('utf8');
        stdin.on('data', option);
    }

    function option(data) {
        const filename = files[Number(data)];
        if (!filename) {
            stdout.write('    \033[33m输入你的选择: \033[39m')
        } else {
            stdin.pause();
            if (stats[Number(data)].isDirectory()) {
                fs.readdir(__dirname + '/' + filename, function (err, files) {
                    console.log('');
                    console.log('   (' + files.length + '  files)');
                    files.forEach(function (file) {
                        console.log('    -  ' + file);
                    });
                    console.log('');
                });
            } else {
                fs.readFile(__dirname + '/' + filename, 'utf8', function (err, data) {
                    console.log('');
                    console.log('\033[90m' + data.replace(/(.*)/g, '    $1') + '\033[39m');
                });
            }
        }
    }

    file(0);
});


    


    



©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,719评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,337评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,887评论 0 324
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,488评论 1 266
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,313评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,284评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,672评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,346评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,644评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,700评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,457评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,316评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,706评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,261评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,648评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,859评论 2 335

推荐阅读更多精彩内容

  • //公共引用 varfs =require('fs'), path =require('path'); 1、读取文...
    才気莮孒阅读 824评论 0 1
  • 个人入门学习用笔记、不过多作为参考依据。如有错误欢迎斧正 目录 简书好像不支持锚点、复制搜索(反正也是写给我自己看...
    kirito_song阅读 2,436评论 1 37
  • 一、Python简介和环境搭建以及pip的安装 4课时实验课主要内容 【Python简介】: Python 是一个...
    _小老虎_阅读 5,689评论 0 10
  • Node.js是目前非常火热的技术,但是它的诞生经历却很奇特。 众所周知,在Netscape设计出JavaScri...
    w_zhuan阅读 3,603评论 2 41
  • 人生若只如初见 你该重新打扮 别再以我喜欢的样子 迷惑我的眼 人生若只如初见 我们也无需交谈 就辜负那心动的瞬间 ...
    8a81955dd0ff阅读 186评论 0 3