06-03-文件目录操作

文件系统

Node.js可以做两件事:

  • 基于命令行的开发
  • Web开发
    其中,基于命令行的开发,主要是针对文件操作。需要涉及到node的fs模块。
    针对web开发,主要是使用了http模块。

针对服务端的编程。最常用最基本的一个功能就是进行文件操作。
任何一门后台语言,都可以对文件进行各种操作。

所谓的文件,通常有如下两种表现形式:

  • 文件,比如html文件、图片文件、音频文件、word文件、txt文件
  • 目录,文件夹,用来存放文件的文件

针对文件和目录,都有相应的操作,

  • 文件:创建、读取、写入、删除、获取文件的信息
  • 目录:创建、读取、删除

在node.js中,提供了一个核心模块 – fs,用于进行文件相关的操作。
中文的手册:http://nodejs.cn/api/
fs是file system的简写,文件系统
关于文件操作,还有一个特殊的地方,几乎所有的文件操作都分为两个版本:
- 异步的
- 同步的
其中,短名称是异步的,后面带有Sync的是同步操作。

常见文件操作

基础方法,都是一些比较底层的方法,功能相对单一,如:

  • open
  • close
  • read
  • write
    高级方法,在基础方法的基础之上进行了再次封装,功能比较强大,如
  • readFile
  • writeFile
  • appendFile
  • stat
  • unlink
  • exists
    在开发的时候,推荐使用高级方法。只要在要实现特定功能或者需要自己来封装,才需要去使用底层的基础方法。

readFile 读取

用于读取文件,有两个版本,异步和同步

readFile 异步的读取文件

06-03-文件目录操作
使用如下:
06-03-文件目录操作
由于没有设置字符串,所以默认是使用buffer的形式(二进制的数据,使用了十六进制的表示方式)
所以,针对文本文件,需要设置utf8字符集。如下
06-03-文件目录操作
其中,node中的回调,通常描述为错误优先的回调。意思是在回调函数中,第一个参数永远都是err,表示错误信息,如果err为null,就表示没有错误。如果不为null,通常就范湖为一个对象,对象中包含错误的详细信息。

readFileSync同步的读取文件

06-03-文件目录操作
方法的返回值就是结果。如果没有错误,就会返回文件的内容,使用如下:
06-03-文件目录操作
需要掌握异步和同步版本的区别:

  • 获取结果的方式不同,异步是使用回调函数的参数来获取的,同步使用方法的返回值
  • 执行的顺序不同

异步的执行顺序:
06-03-文件目录操作
同步的执行顺序:
06-03-文件目录操作

writeFile 写入

作用:用于将指定的内容写入文件,采取的覆盖式写入
有如下两个版本:

  • 异步
  • 同步

writeFile异步写入

06-03-文件目录操作
使用如下
06-03-文件目录操作
当前1.txt是已经存在的文件。
如果文件不存在呢?代码如下:
06-03-文件目录操作
针对写入操作,如果文件存在,直接删除文件中的所有内容,然后写入新的内容。
如果文件不存在,会创建一个新文件,然后写入新的内容。

writeFileSync 同步写入

06-03-文件目录操作
06-03-文件目录操作

appendFile 追加

append的意思是指追加。
作用:用于以追加的方式向文件中写入内容。
分为两个版本:

  • 异步
  • 同步

appendFile 异步

06-03-文件目录操作
使用如下:
06-03-文件目录操作

appendFileSync 同步

06-03-文件目录操作
使用如下:
06-03-文件目录操作
如果文件不存在的情况,其用法就相当于是writeFile。

stat 获取文件的信息

作用:用于获取文件的信息。
分为两个版本:

  • 异步
  • 同步
    任何一个文件,它都有一些基本信息,如下
    06-03-文件目录操作
    使用stat方法,就可以获取文件的基本信息。
    使用异步的方式来获取文件信息,
    06-03-文件目录操作

使用如下06-03-文件目录操作
其中,mode表示文件的类型,33206说明这是一个文件。16822则说明是一个目录,如下
06-03-文件目录操作
其实,使用stat方法,得到的stats结果,是一个对象。除了有上述列出的这些属性之外,它还有一些方法,如下:
06-03-文件目录操作
06-03-文件目录操作
可以利用这两个方法来判断文件的类型。
其他的方法都是在Linux系统下才会使用到的。

unlink 删除文件

作用:用于删除指定的文件
用两个版本:

  • 异步
  • 同步
    异步的用法,06-03-文件目录操作
    使用如下:
    06-03-文件目录操作

exists 判断文件是否存在

作用:用于判断指定的文件是否存在
注意,exists比较特殊,本身确实是提供了异步和同步的两个版本。
但是,实际上这个操作只是想得到true和false的结果,从而决定下一步该如何操作。
06-03-文件目录操作

针对这个方法,只需要使用同步的版本即可。
在删除文件的时候,如果文件不存在
针对异步的删除,如下:
06-03-文件目录操作

如果是同步的代码,如下:
06-03-文件目录操作
由于同步没有错误处理机制,所以直接报错了,后续代码就不再执行。
此时,应该对文件的存在性进行判断,从而进行相应的处理,如下:
06-03-文件目录操作
其实,读取文件也存在类似的情况。
异步的,没有什么问题。
06-03-文件目录操作
但是,同步呢
06-03-文件目录操作
针对这种情况,需要使用existsSync来进行判断,如下:
06-03-文件目录操作

关于文件操作的几点说明

同步和异步的区别

在fs模块中,所有的操作都提供异步和同步的版本。(除了exists将异步版本废弃了)。
主要有如下两个区别:

  • 程序执行的顺序
  • 获取操作结果的方式,同步使用方法的返回时,异步是使用回调函数的参数
    除此之外,还有一个小区别,对待错误的处理方式。
  • 同步,无法处理错误的,如果程序出错了,就直接报错,代码不会再执行
  • 异步,可以优雅的处理错误,如果程序出错了,会通过回调函数的第一个参数err来获取错误,同时代码可以继续执行下去。

路径的写法

在fs模块中,对文件进行操作,文件的路径,可以有如下两种方式:

  • 相对路径
  • 绝对路径

相对路径

有如下三种写法:
06-03-文件目录操作
06-03-文件目录操作
06-03-文件目录操作
其中./表示当前目录,可以省略,所以第一种和第二种是一样的。

绝对路径

在文件操作中,绝对路径就是指以磁盘开头的路径。如下:
06-03-文件目录操作
注意,尽量使用/。
这个绝对路径的用法是比较死板,这样的代码没有任何可移植性,不好,不建议将其写死了。
其实,在node.js中,有一个全局变量 __dirname,就表示当前文件所处的路径,得到的是一个绝对路径。
__dirname 是一个特殊的魔术变量。会自动的获取到当前文件的绝对路径。
06-03-文件目录操作
如此一来,就可以自己去拼接路径,如下:
06-03-文件目录操作
这样还不够方便,还需要自己处理字符串的拼接,尤其是\,一不小心就出错了。
针对路径的处理,node.js提供了一个path模块,专门用来处理路径的。
其中提供了一个方法 — join,用于多个路径的连接。
path.join(字符串1,字符串2,字符串n);
06-03-文件目录操作
一般而言,能使用相对路径,就不需要使用绝对路径。
但是有些场合,必须要使用绝对路径。

err的意义及简化写法

主要是针对异步的文件操作。
错误优先的回调函数
在异步的操作中,回调函数的第一个参数就是err参数
如果没有任何错误,err的值就是null。
06-03-文件目录操作
如果有错误,它就是一个错误对象,如下:
06-03-文件目录操作
针对err的处理,在node.js中,可以使用throw语句来简化,如下:
06-03-文件目录操作

常见目录操作

针对目录,主要有如下三个操作:

  • 创建目录
  • 删除目录
  • 读取目录

创建目录

使用mkdir方法来实现的。mk是make的简写。
有异步和同步两个版本。
以异步版本来说明其用法。
06-03-文件目录操作
其中,mode表示这个文件的权限,可读可写可执行。在windows下无效,不用理会。
编写代码如下:
06-03-文件目录操作
mkdir只能创建单层目录,也就说说,每一次只能创建一个目录。
06-03-文件目录操作
由于blog是不存在的,你想在blog下面创建css目录,就意味着需要先创建blog,然后再来创建css目录,由于mkdir只能创建单层目录,所以报错了。
如果blog本身已经存在了,mkdir(‘blog/css’)这样是可以的。如下:
06-03-文件目录操作

在使用mkdir创建目录时,如果该目录已经存在了,就会报错,如下:
06-03-文件目录操作
针对这种情况,可以使用exists来判断,如下:
06-03-文件目录操作

删除目录

使用rmdir来实现的,rm是remove的简写。
也有异步和同步两个版本。
异步版本,如下:
06-03-文件目录操作
使用如下:
06-03-文件目录操作
注意:**rmdir只能删除空目录,如果目录还有内容,就不能删除,**如下:
06-03-文件目录操作
如果某个目录是非空的,就需要将目录中的内容先删除掉,然后再来删除目录本身。
针对目录中的内容,我们需要通过代码来获取,此时,就需要用到目录的读取操作。

读取目录

使用readdir来实现的。
也有异步和同步两个版本。
以异步为例说明,如下:
06-03-文件目录操作
使用如下:
06-03-文件目录操作
读取到的结果files是一个数组,将所有的文件名和目录名都列出了。
有了readdir,就可以删除非空目录,
目录结构如下,其中css、iamges和js的目录都是空的
06-03-文件目录操作
编写代码如下:
06-03-文件目录操作
测试,ok。

删除多层目录

假如有如下目录结构:
06-03-文件目录操作
目标,需要删除blog目录。
分析:经过分析发现,在遍历blog目录的时候,如果是文件,直接删除即可,如果是目录,需要重复这个blog的处理过程,这就是递归。

只要是递归,那就必须要定义函数。
可以定义一个函数,removeDir,给定一个参数路径。
可以尝试着编写代码如下:
06-03-文件目录操作
执行如下:
06-03-文件目录操作
之所以出现这个错误,是由于我们使用的异步的文件操作,同时又有递归,所以这个程序执行的顺序和书写的顺序是不一致的。

针对这种情况我们最好使用同步的方式来操作文件,可以更彻底一点,都使用同步来操作。
编写代码如下:
06-03-文件目录操作
测试,ok。
在使用fs操作文件的时候,到底是使用异步还是同步?
在实际开发时,90%以上都是使用异步的。只在一些特殊的场合下才使用同步,比如递归删除,需要使用异步。

文件操作实战应用

要实现如下两个:

  • 自动构建项目结构
  • 自动合并代码

自动构建项目结构

在今天的前端开发中,有很多自动化的工具(脚手架),可以帮助我们快速的搭建项目结构。比如:

  • vue-cli
  • express-generator
  • create-react-app
    其实,这些工具都是基于node,都是使用node.js来开发的。
    任何一个脚手架工具,它生成的项目结构是固定的。
    所以,我们可以使用一个配置文件,将这个结构固定起来。
    比如,要实现一个最简单的项目结构
    blog
    -----js
    -----css
    -----images
    -----index.html
    可以定义一个文件来保存这个项目结构。使用json文件最合适。
    如下:
    06-03-文件目录操作
    对应的,编写代码如下:
    06-03-文件目录操作
    测试,ok,创建的目录结构如下:
    06-03-文件目录操作
    目录结构及文件都已经创建成功,但是有一点,就是html文件的格式不太友好,需要处理一下。
    可以使用转移字符来实现,换行使用\n即可,缩进使用\t即可
    06-03-文件目录操作
    再次测试,如下:
    06-03-文件目录操作
    ok了。
    任务:
    总结一下,js的模块化开发
    递归删除多级目录
    自动构建项目结构
    自动合并代码 (需要使用watch方法),如下:
    06-03-文件目录操作

自动合并代码—第一步

在开发的时候,我们通常会在src目录下进行代码的编写。
每次编写完毕,一保存,会自动进行打包,比如webpack。
需要分步完成:
合并代码
自动合并代码
准备目录结构,如下:
06-03-文件目录操作
目标就是,将js目录下,所有的文件进行合并。
在js目录下,创建两个文件,如下:
06-03-文件目录操作
06-03-文件目录操作
可以编写代码如下:
06-03-文件目录操作
执行,结果如下:
06-03-文件目录操作
结果是空的,why?
是异步导致的。读取操作还没有完成,就已经执行了写入操作,导致最后写入的是空。
解决办法有二:
每读取一个文件,就保存一个内容
使用同步的方式来解决
采用同步的读取方式来解决,如下:
06-03-文件目录操作
再次执行,结果ok,如下:
06-03-文件目录操作
针对当前这个代码,需要每次手动的去执行命令,实现合并代码。
能否实现自动合并呢?
要实现自动合并代码,就需要利用fs的watch方法。

watch方法

作用:就是用于监视某个文件/目录的变化。
格式:
06-03-文件目录操作
06-03-文件目录操作
Listener是一个函数,事件处理函数,一旦filename发生变化了,就会执行该函数。有两个参数:
type:事件的类型
name:改变的文件名称
对于type而言,新建文件、删除文件、重命名文件都叫rename,修改文件则叫做change。
默认情况下,只能监视当前目录下的所有文件,如果还有子目录,不予监视,此时可以设置recursive=true来递归监视。
有了watch,就可以完成自动合并。

结合watch实现自动合并代码功能

编写代码如下:
06-03-文件目录操作
注意:在测试的时候,需要使用另外一个编辑器来编写文件。

关于异步执行的说明

同步是按照代码书写的顺序依次执行的,而异步则不是。
实际上,在遇到有异步代码的时候,将异步中的回调函数放到任务队列中,然后在所有同步任务执行完毕之后,开始遍历任务队列,遍历的时候,看哪个任务满足执行条件了,就开始执行,而不是按照放入的顺序来执行的。
比如,如下代码:
06-03-文件目录操作
绘图如下:
06-03-文件目录操作
在对任务队列进行循环的时候,它是需要看每个任务是否满足条件,一旦满足立即执行,然后继续重复这个过程。
请问:setTimeout是不是到了指定时间必须要执行呢?
不是的。