## 文件系统处理
我们在开发命令程序的时候,经常需要对文件做各种操作,比如,创建文件、删除文件、修改文件内容、遍历文件等。虽然`NodeJs`内置了`fs`模块供我们使用,但是其功能相对有限,这里,我将介绍一个第三方扩展模块[fs-extra](https://www.npmjs.com/package/fs-extra)。
[TOC]
### fs-extra模块与fs模块的关系
`fs-extra`模块是`fs`模块的扩充。也就是说,使用`fs-extra`模块可以做到以下两点:
1.直接调用`fs`模块所有方法(所以,你可以完全抛弃`fs`模块)
2.调用`fs-extra`扩充的方法
### fs-extra模块的安装
* 全局安装
~~~
$ npm install fs-extra -g
~~~
* 本地安装
将当前工作目录切换为需要使用`fs-extra`模块的目录(已经创建了`package.json`文件的目录)
~~~
$ npm install fs-extra --save
~~~
### 同步操作与异步操作
在介绍具体方法之前,有必要介绍一下文件系统的同步(`Sync`)方法和异步(`Async`)方法。
* 同步方法——只有等被调用方法执行结果返回之后,才会执行方法后面代码,执行过程是阻塞的。
* 异步方法——方法被调用后无需等待结果返回,直接执行后面代码,执行结果以回调的方式返回。
`fs-extra`的方法基本上都有一个同步和一个异步的方法,它们的方法定义也非常有规律,**异步方法只需在同步方法的基础上去掉名字的`Sync`,最后一个参数改为回调方法即可**,下面是一个例子:
~~~
var fse = require('fs-extra');
///异步方法
fse.emptyDir('mydir', function(err){
if (err) return console.error(err)
console.log('success!')
})
//同步方法
fse.emptyDirSync('mydir');
~~~
***为了文章的简介,下面介绍的常用方法,只介绍同步方法,不介绍异步方法,需要用到的时候,你可以根据规律自行补全。***
### fs-extra常用方法
#### copySync
**将源文件(或目录)拷贝到目标文件(或目录)**
* `copySync(src, dest, [options])`
* `src <String>`: 源文件或目录路径
* `dest <String>`: 目标文件或目录路径
* `options <Object>`: 可选参数,JSON对象
* `overwrite <boolean>`: 覆盖目标文件或目录,默认值为true,注意,如果设为false,而目标文件又恰好存在,复制会失败,但不会抛出异常。
* `errorOnExist <boolean>`: 当overwritef为alse时,设置errorOnExist为true,copy出错时会抛出异常
* `dereference <boolean>`: 解除符号连接,默认值为false
* `preserveTimestamps <boolean>`: 如果设为true,源文件的最后修改和访问时间将被重新设置,默认值是false
* `filter <Function>`: 过滤拷贝文件的方法,已废弃,忽略它
例子:
~~~
var fs = require('fs-extra')
// 文件拷贝
fs.copySync('/tmp/myfile', '/tmp/mynewfile')
// 目录拷贝,包含子目录和子文件
fs.copySync('/tmp/mydir', '/tmp/mynewdir')
~~~
#### emptyDirSync
**确保目标目录是空的,如果目录不是空的,删除目录里所有内容;如果目录不存在,创建目录。目录本身不删除。**
* `emptyDirSync(dir)`
* `dir <String>`:目标目录路径
例子:
~~~
var fs = require('fs-extra')
//确保最终存在一个空的目录
fs.emptyDirSync('/tmp/some/dir')
~~~
#### ensureFileSync
**确保目标文件存在,如果目标文件父级目录不存在,则父级目录也将被创建,如果目标文件存在,不作为。**
* `ensureFileSync(file)`
* `file <String>`:目标文件路径
例子:
~~~
var fs = require('fs-extra')
var file = '/tmp/this/path/does/not/exist/file.txt'
//创建文件file.txt,如果他的父级目录/tmp/this/path/does/not/exist/有不存在的,则目录也创建
fs.ensureFileSync(file)
~~~
#### ensureDirSync
**确保目标目录存在,如果目标目录不存在,则创建目标目录,如果目标目录存在,不作为。**
例子:
* `ensureDirSync(dir)`
* `dir <String>`:目标目录路径
~~~
var fs = require('fs-extra')
var dir = '/tmp/this/path/does/not/exist'
fs.ensureDirSync(dir)
~~~
#### mkdirsSync
**不推荐使用,直接用`ensureDirSync`代替这个方法**
#### moveSync
**将源文件(或目录)移动(剪切)到目标文件(或目录)**
* `moveSync(src, dest, [options])`
* `src <String>`: 源文件或目录路径
* `dest <String>`: 目标文件或目录路径
* `options <Object>`: 可选参数,JSON对象
* `overwrite <boolean>`: 覆盖已存在的文件或目录,默认值为false
例子:
~~~
var fs = require('fs-extra')
fs.moveSync('/tmp/somefile', '/tmp/does/not/exist/yet/somefile')
~~~
~~~
var fs = require('fs-extra')
//使用overwrite参数
fs.moveSync('/tmp/somedir', '/tmp/may/already/existed/somedir', { overwrite: true })
~~~
#### outputFileSync
**类似[writeFileSync](),唯一的区别是:当父级目录不存在时,使用outputFileSync,这些目录会被逐层创建,而使用writeFileSync时,会报错**
* `outputFileSync(file, data, [options])`
* `file <String>`: 目标文件路径
* `data <String> | <Buffer> | <Uint8Array>`: 写入目标文件的内容
* `options <Object> | <String>`: 和[fs.writeFileSync()](https://nodejs.org/api/fs.html#fs_fs_writefilesync_file_data_options)方法的option参数一样
例子:
~~~
var fs = require('fs-extra')
var file = '/tmp/this/path/does/not/exist/file.txt'
fs.outputFileSync(file, 'hello!')
var data = fs.readFileSync(file, 'utf8')
console.log(data) // => hello!
~~~
#### outputJsonSync
**类似[writeJsonSync](https://github.com/jprichardson/node-fs-extra/blob/master/docs/writeJson-sync.md),唯一的区别是:当父级目录不存在时,使用`outputJsonSync`,这些目录会被逐层创建,而使用`writeJsonSync`时,会报错**
* `outputJsonSync(file, object, [options])`
* `file <String>`: 目标文件路径
* `object <Object>`: 写入目标文件的JSON对象
* `options <Object>`: 和[jsonFile.writeFileSync()](https://github.com/jprichardson/node-jsonfile#writefilesyncfilename-obj-options)方法的option参数一样
例子:
~~~
var fs = require('fs-extra')
var file = '/tmp/this/path/does/not/exist/file.json'
fs.outputJsonSync(file, {name: 'JP'})
var data = fs.readJsonSync(file)
console.log(data.name) // => JP
~~~
#### readJsonSync
**读取一个`JSON`文件并解析为一个`JSON`对象**
* `readJsonSync(file,[options])`
* `file <String>`: 源文件路径
* `options <Object>`: 可选参数,JSON对象
例子:
~~~
var fs = require('fs-extra')
var packageObj = fs.readJsonSync('./package.json')
console.log(packageObj.version) // => 1.0.0
~~~
~~~
var fs = require('fs-extra')
var file = '/tmp/some-invalid.json'
var data = '{not valid JSON'
fs.writeFileSync(file, data)
/*默认情况下,readJsonSync()方法校验读取文件的内容,如果文件内容不符合JSON的格式,会抛出异常。如果,将throw参数设为false,则可以阻止异常抛出*/
var obj = fs.readJsonSync(file, { throws: false })
console.log(obj) // => null
~~~
#### removeSync
**删除目标文件或目录,即时目录里面有内容,像命令行使用 `rm -rf `一样的效果**
* `removeSync(path)`
* `path <String>`:目标文件或目录路径
例子:
~~~
var fs = require('fs-extra')
// 删除文件
fs.removeSync('/tmp/myfile.txt')
//删除outsider整个目录
fs.removeSync('/home/outsider')
~~~
#### writeJsonSync
**将一个对象写入`JSON`文件,推荐使用[outputJsonSync()]()**
* `writeJsonSync(file, object, [options])`
* `file <String>`: 目标文件路径
* `object <String>`: 写入目标文件的JSON对象
* `options <Object>`: 和[jsonFile.writeFileSync()](https://github.com/jprichardson/node-jsonfile#writefilesyncfilename-obj-options)方法的option参数一样
例子:
~~~
var fs = require('fs-extra')
fs.writeJsonSync('./package.json', {name: 'fs-extra'})
~~~
>Tips:
对于命令行程序的开发,上面介绍的方法基本够用了,遍历文件的操作会在[增强sell](增强sell.md)介绍,用shelljs实现起来更简单。
### fs-extra和f's API地址
**`fs-extra`官网:https://www.npmjs.com/package/fs-extra**
**`fs API`:http://nodejs.cn/api/fs.html**