fs概述

文件 I/O 是由简单封装的标准 POSIX 函数提供的。 nodeJS中通过 require('fs') 使用fs模块。 所有的方法都有异步和同步的形式。

异步形式始终以完成回调作为它最后一个参数。 传给完成回调的参数取决于具体方法,但第一个参数总是留给异常。 如果操作成功完成,则第一个参数会是 null 或 undefined

//异步示例
var fs = require('fs');
fs.unlink('/tmp/hello', function(err){
 if (err) throw err;
 console.log('successfully deleted /tmp/hello');
});

当使用同步形式时,任何异常都会被立即抛出。 可以使用 try/catch 来处理异常,或让它们往上冒泡

//同步示例
var fs = require('fs');
fs.unlinkSync('/tmp/hello');
console.log('successfully deleted /tmp/hello');

异步方法不保证执行顺序。 所以下面的例子容易出错

fs.rename('/tmp/hello', '/tmp/world', function(err){
 if (err) throw err;
 console.log('renamed complete');
});
fs.stat('/tmp/world', function(err, stats){
 if (err) throw err;
 console.log('stats: ${JSON.stringify(stats)}');
});

fs.stat 可能在 fs.rename 之前执行。正确的方法是把回调链起来

fs.rename('/tmp/hello', '/tmp/world', function(err){
 if (err) throw err;
 fs.stat('/tmp/world', function(err, stats){
  if (err) throw err;
  console.log('stats: ${JSON.stringify(stats)}');
 });
});

推荐开发者使用这些函数的异步版本。 同步版本会阻塞整个进程,直到它们完成(停止所有连接)

fs底层操作

1、打开文件 fs.open(path, flags[, mode], callback)

参数如下:

path <String> | <Buffer>

flags <String> | <Number>

mode <Integer> 设置文件模式(权限和 sticky 位),但只有当文件被创建时才有效。默认为 0666,可读写

callback <Function> 该回调有两个参数 (err错误, fd文件标识,与定时器标识类似)

flags可以是:

'r' - 以读取模式打开文件。如果文件不存在则发生异常。

'r+' - 以读写模式打开文件。如果文件不存在则发生异常。

'rs+' - 以同步读写模式打开文件。命令操作系统绕过本地文件系统缓存。

'w' - 以写入模式打开文件。文件会被创建(如果文件不存在)或截断(如果文件存在)。

'wx' - 类似 'w',但如果 path 存在,则失败。

'w+' - 以读写模式打开文件。文件会被创建(如果文件不存在)或截断(如果文件存在)。

'wx+' - 类似 'w+',但如果 path 存在,则失败。

'a' - 以追加模式打开文件。如果文件不存在,则会被创建。

'ax' - 类似于 'a',但如果 path 存在,则失败。

'a+' - 以读取和追加模式打开文件。如果文件不存在,则会被创建。

'ax+' - 类似于 'a+',但如果 path 存在,则失败。

[注意]使用'rs+'模式不会使fs.open()进入同步阻塞调用。如果那是你想要的,则应该使用fs.openSync()

var fs = require('fs');

fs.open('a.txt','r',function(err,fs){

  console.log(err);//null

  console.log(fs);//3

})
var fs = require('fs');
fs.open('b.txt','r',function(err,fs){
/*
{ Error: ENOENT: no such file or directory, open 'D:\project\b.txt'
  at Error (native)
 errno: -4058,
 code: 'ENOENT',
 syscall: 'open',
 path: 'D:\\project\\b.txt' }
 */
  console.log(err);
  console.log(fs);//undefined
})

文件的回调函数中的第二个参数fd代表文件标识,与定时器标识类似,用于标识文件,且随着文件的打开顺序递增

var fs = require('fs');

fs.open('1.txt','r',function(err,fs){

  console.log(fs);//3

})

fs.open('2.txt','r',function(err,fs){

  console.log(fs);//4

})

fs.openSync(path, flags[, mode])

fs.open() 的同步版本。 返回一个表示文件描述符的整数

var fs = require('fs');

var result = fs.openSync('1.txt','r');

console.log(result);//3

2、读取文件 fs.read(fd, buffer, offset, length, position, callback)

参数如下:

fd <Integer> 通过 fs.open() 方法返回的文件描述符

buffer <String> | <Buffer> 数据将被写入到buffer

offset <Integer> buffer中开始写入的偏移量

length <Integer> 指定要读取的字节数(整数)

position <Integer> 指定从文件中开始读取的位置(整数)。 如果position为null,则数据从当前文件位置开始读取

callback <Function> 回调有三个参数 (err, bytesRead, buffer)。err为错误信息,bytesRead表示读取的字节数,buffer为缓冲区对象

由于使用read()方法,会将文件内容读取buffer对象中,所以需要提前先准备一个buffer对象

var fs = require('fs');
fs.open('1.txt','r',function(err,fd){
  if(err){
    console.log('文件打开失败');
  }else{
    var bf = Buffer.alloc(5);
    fs.read(fd,bf,0,3,null,function(err,len,buffer){
      console.log(err);//null
      console.log(len);//3
      console.log(buffer);//<Buffer 61 61 61 00 00>
    })
  }
});

fs.readSync(fd, buffer, offset, length, position)

fs.read() 的同步版本,返回 bytesRead 的数量

var fs = require('fs');

var fd = fs.openSync('1.txt','r');

var bf = Buffer.alloc(5);

var result = fs.readSync(fd,bf,0,3,null);

console.log(result);//3

3、写入文件 fs.write(fd, buffer, offset, length[, position], callback)

参数如下

fd <Integer>"htmlcode">

var fs = require('fs');
fs.open('1.txt','r+',function(err,fd){
  if(err){
    console.log('文件打开失败');
  }else{
    var bf = Buffer.from('test');
    fs.write(fd,bf,0,3,null,function(err,len,buffer){
      console.log(err);//null
      console.log(len);//3
      console.log(buffer);//<Buffer 74 65 73 74>
    })
  }
});

fs.write(fd, data[, position[, encoding]], callback)

该方法写入data到fd指定的文件。如果data不是一个Buffer实例,则该值将被强制转换为一个字符串

不同于写入 buffer,该方法整个字符串必须被写入。不能指定子字符串,这是因为结果数据的字节偏移量可能与字符串的偏移量不同

fd"htmlcode">

var fs = require('fs');
fs.open('1.txt','r+',function(err,fd){
  if(err){
    console.log('文件打开失败');
  }else{
    fs.write(fd,'12345',function(err,len,str){
      console.log(err);//null
      console.log(len);//5
      console.log(str);//<Buffer 74 65 73 74>
    })
  }
});

fs.writeSync()

fs.write() 的同步版本。返回写入的字节数

var fs = require('fs');
var fd = fs.openSync('1.txt','r+');
var bf = Buffer.alloc(5);
var result = fs.writeSync(fd,bf,0,3,null);
console.log(result);//3

4、关闭文件 fs.close(fd, callback)

一个文件被操作后,要及时将该文件关闭

参数如下:

fd - 通过 fs.open() 方法返回的文件描述符。

callback - 回调函数,没有参数。

var fs = require('fs');
fs.open('1.txt','r+',function(err,fd){
  if(err){
    console.log('文件打开失败');
  }else{
    fs.close(fd, function(err){
      if (err){
        console.log(err);
      } 
      console.log("文件关闭成功");
    });
  }
});

fs.closeSync(fd)

fs.close(fd, callback)的同步版本,返回undefined

var fs = require('fs');

var fd = fs.openSync('1.txt','r+');

fs.closeSync(fd);

File操作

上一部分介绍的都是些底层的操作,接下来将介绍一些更便捷的文件操作。使用下列方法的时候,不需要再打开和关闭文件,直接操作即可

1、写入文件

fs.writeFile(file, data[, options], callback)

异步的将数据写入一个文件,如果文件不存在则新建,如果文件原先存在,会被替换

参数如下:

file - 文件名或文件描述符。

data - 要写入文件的数据,可以是 String(字符串) 或 Buffer(流) 对象。

options - 该参数是一个对象,包含 {encoding, mode, flag}。默认编码为 utf8, 模式为 0666 , flag 为 'w'

callback - 回调函数,回调函数只包含错误信息参数(err),在写入失败时返回。

var fs = require('fs');

var filename = '1.txt';

fs.writeFile(filename,'hello',function(err){

  console.log(err);//null

})

fs.writeFileSync(file, data[, options])

fs.writeFile() 的同步版本。返回 undefined

var fs = require('fs');

var filename = '1.txt';

fs.writeFileSync(filename,'abc');

2、追加文件

fs.appendFile(filename, data, [options], callback)

异步地追加数据到一个文件,如果文件不存在则创建文件。 data 可以是一个字符串或 buffer

参数如下

file - 文件名或文件描述符。

data - 要写入文件的数据,可以是 String(字符串) 或 Buffer(流) 对象。

options - 该参数是一个对象,包含 {encoding, mode, flag}。默认编码为 utf8, 模式为 0666 , flag 为 'w'

callback - 回调函数,回调函数只包含错误信息参数(err),在写入失败时返回。

var fs = require('fs');

var filename = '1.txt';

fs.appendFile(filename,' world',function(err){

  console.log(err);//null

})

fs.appendFileSync(file, data[, options])

fs.appendFile()的同步版本。返回undefined

var fs = require('fs');

var filename = '1.txt';

fs.appendFileSync(filename,' lalala');

3、读取文件

fs.readFile(file[, options], callback)

参数如下

file - 文件名或文件描述符

options - 该参数是一个对象,包含 {encoding, flag}。默认编码为null,即如果字符编码未指定,则返回原始的 buffer;flag默认为'r'

callback - 回调函数,回调有两个参数 (err, data),其中data是文件的内容(buffer对象),err是错误信息参数,在写入失败时返回

var fs = require('fs');
var filename = '1.txt';
fs.readFile(filename,function(err,data){
  if(err){
    console.log('文件读取失败');
  }else{
    console.log(data);//<Buffer 61 62 63 20 77 6f 72 6c 64 20 6c 61 6c 61 6c 61>
    console.log(data.toString());//'abc world lalala'
  }
});

fs.readFileSync(file[, options])

fs.readFile的同步版本。返回file的内容

如果指定了encoding选项,则该函数返回一个字符串,否则返回一个buffer

var fs = require('fs');
var filename = '1.txt';
var result = fs.readFileSync(filename);
console.log(result);//<Buffer 61 62 63 20 77 6f 72 6c 64 20 6c 61 6c 61 6c 61>
console.log(result.toString());'abc world lalala'

4、删除文件

fs.unlink(path, callback)

参数如下:

path - 文件路径。

callback - 回调函数,没有参数。

var fs = require('fs');
var filename = '1.txt';
fs.unlink(filename, function(err) {
  if (err) {
    return console.log('删除失败');
  }
  console.log("删除成功");
});

fs.unlinkSync(path)

fs.unlink(path, callback)的同步版本,返回值为undefined

var fs = require('fs');

var filename = '1.txt';

fs.unlink(filename);

5、重命名

fs.rename(oldPath, newPath, callback)

参数如下:

oldPath <String> | <Buffer>

newPath <String> | <Buffer>

callback <Function> 回调只有一个可能的异常参数

var fs = require('fs');
var filename = 'a.txt';
fs.rename(filename,'2.new.txt',function(err){
  console.log(err);//null
})

fs.renameSync(oldPath, newPath)

fs.rename(oldPath, newPath, callback)的同步版本,返回undefined

var fs = require('fs');

var filename = '2.new.txt';

var result = fs.renameSync(filename,'a.txt');

6、文件信息

fs.stat(path, callback)

fs.stat()执行后,会将stats类的实例返回给其回调函数。可通过stats类中的提供方法判断文件的相关属性

参数如下:

path - 文件路径。

callback - 回调函数,带有两个参数如:(err, stats), stats 是 fs.Stats 对象

var fs = require('fs');
var filename = 'a.txt';
fs.stat(filename,function(err,stats){
  console.log(err);//null
/*
{ dev: 223576,
 mode: 33206,
 nlink: 1,
 uid: 0,
 gid: 0,
 rdev: 0,
 blksize: undefined,
 ino: 7599824371527537,
 size: 0,
 blocks: undefined,
 atime: 2017-06-03T14:18:15.370Z,
 mtime: 2017-06-03T14:18:15.370Z,
 ctime: 2017-06-03T16:32:05.776Z,
 birthtime: 2017-06-03T14:18:15.370Z }
 */  
  console.log(stats);
});

stats类中的方法有

stats.isFile()"htmlcode">

var fs = require('fs');
var filename = 'a.txt';
fs.stat(filename,function(err,stats){
  console.log(stats.isFile());//true
});

fs.statSync(path)

fs.stat(path, callback)方法的同步版本,返回一个 fs.Stats 实例

var fs = require('fs');
var filename = 'a.txt';
var result = fs.statSync(filename);
/*
{ dev: 223576,
 mode: 33206,
 nlink: 1,
 uid: 0,
 gid: 0,
 rdev: 0,
 blksize: undefined,
 ino: 7599824371527537,
 size: 0,
 blocks: undefined,
 atime: 2017-06-03T14:18:15.370Z,
 mtime: 2017-06-03T14:18:15.370Z,
 ctime: 2017-06-03T16:32:05.776Z,
 birthtime: 2017-06-03T14:18:15.370Z }
 */
console.log(result);

7、监听

fs.watch(filename[, options][, listener])

该方法用于监视filename的变化,filename可以是一个文件或一个目录。返回的对象是一个fs.FSWatcher

参数如下

filename <String> | <Buffer>

options <String> | <Object> 参数可选,如果options是一个字符串,则它指定了encoding。否则options应该以一个对象传入

"htmlcode">

fs.watch('somedir', (eventType, filename) => {
 console.log(`事件类型是: ${eventType}`);
 if (filename) {
  console.log(`提供的文件名: ${filename}`);
 } else {
  console.log('未提供文件名');
 }
});
var fs = require('fs');
var filename = '1.txt';
fs.watch(filename,function(eventType, _filename){
  console.log(eventType);//change
  if(_filename){
    console.log(_filename + '发生了改变');//'1.txt发生了改变'
  }else{
    console.log('...');
  }
  
})

[注意]当一个文件出现或消失在一个目录里时,'rename'也会被触发

fs目录操作

1、创建

fs.mkdir(path[, mode], callback)

参数如下:

path - 文件路径。

mode - 设置目录权限,默认为 0777。

callback - 回调函数,回调只有一个可能的异常参数

var fs = require('fs');
fs.mkdir('./1',function(err){
  console.log(err);//null
})

fs.mkdirSync(path[, mode])

fs.mkdir(path[, mode], callback)的同步版本,返回undefined

var fs = require('fs');
fs.mkdirSync('./2');

2、删除

fs.rmdir(path, callback)

参数如下:

path - 文件路径。

callback - 回调函数,回调只有一个可能的异常参数

var fs = require('fs');
fs.rmdir('./1',function(err){
  console.log(err);//null
})

fs.rmdirSync(path, callback)

fs.rmdir(path, callback)的同步版本,返回undefined

var fs = require('fs');
fs.rmdirSync('./2');

3、读取

fs.readdir(path[, options], callback)

参数如下:

path <String> | <Buffer>

options <String> | <Object> 可选的 options 参数用于传入回调的文件名,它可以是一个字符串并指定一个字符编码,或是一个对象且由一个 encoding 属性指定使用的字符编码。 如果 encoding 设为 'buffer',则返回的文件名会被作为 Buffer 对象传入

"htmlcode">

var fs = require('fs');
fs.readdir('./',function(err,data){
  console.log(err);//null
/*
[ '.csslintrc',
 '.jshintrc',
 'a.txt',
 'dist',
 'Gruntfile.js',
 'Gruntfile1.js',
 'index.html',
 'main.js',
 'node_modules',
 'package.json',
 'src' ]
 */
  console.log(data);
})
var fs = require('fs');
fs.readdir('./',function(err,data){
  data.forEach(function(item,index,arr){
    fs.stat(item,function(err,stats){
      if(stats.isFile()){
        console.log('文件:' + item);
      }
      if(stats.isDirectory()){
        console.log('目录:' + item);
      }
    });  
  })
})
/*
文件:.jshintrc
文件:.csslintrc
目录:dist
文件:Gruntfile.js
文件:index.html
文件:Gruntfile1.js
文件:main.js
目录:node_modules
文件:package.json
文件:a.txt
目录:src
 */

fs.readdirSync(path[, options], callback)

fs.readdir(path[, options], callback)的同步版本,返回一个不包括 '.' 和 '..' 的文件名的数组

var fs = require('fs');
var result = fs.readdirSync('./');
/*
[ '.csslintrc',
 '.jshintrc',
 'a.txt',
 'dist',
 'Gruntfile.js',
 'Gruntfile1.js',
 'index.html',
 'main.js',
 'node_modules',
 'package.json',
 'src' ]
 */
console.log(result);

遍历目录

遍历目录是操作文件时的一个常见需求。比如写一个程序,需要找到并处理指定目录下的所有JS文件时,就需要遍历整个目录

遍历目录时一般使用递归算法,否则就难以编写出简洁的代码。递归算法与数学归纳法类似,通过不断缩小问题的规模来解决问题

function factorial(n) {
  if (n === 1) {
    return 1;
  } else {
    return n * factorial(n - 1);
  }
}

上边的函数用于计算N的阶乘(N!)。可以看到,当N大于1时,问题简化为计算N乘以N-1的阶乘。当N等于1时,问题达到最小规模,不需要再简化,因此直接返回1

目录是一个树状结构,在遍历时一般使用深度优先+先序遍历算法。深度优先,意味着到达一个节点后,首先接着遍历子节点而不是邻居节点。先序遍历,意味着首次到达了某节点就算遍历完成,而不是最后一次返回某节点才算数。因此使用这种遍历方式时,下边这棵树的遍历顺序是A > B > D > E > C > F

"htmlcode">

function travel(dir, callback) {
  fs.readdirSync(dir).forEach(function (file) {
    var pathname = path.join(dir, file);
    if (fs.statSync(pathname).isDirectory()) {
      travel(pathname, callback);
    } else {
      callback(pathname);
    }
  });
}

可以看到,该函数以某个目录作为遍历的起点。遇到一个子目录时,就先接着遍历子目录。遇到一个文件时,就把文件的绝对路径传给回调函数。回调函数拿到文件路径后,就可以做各种判断和处理。因此假设有以下目录

- /home/user/

"htmlcode">

travel('/home/user', function (pathname) {
  console.log(pathname);
});
------------------------
/home/user/foo/x.js
/home/user/bar/y.js
/home/user/z.css

如果读取目录或读取文件状态时使用的是异步API,目录遍历函数实现起来会有些复杂,但原理完全相同。travel函数的异步版本如下

function travel(dir, callback, finish) {
  fs.readdir(dir, function (err, files) {
    (function next(i) {
      if (i < files.length) {
        var pathname = path.join(dir, files[i]);
        fs.stat(pathname, function (err, stats) {
          if (stats.isDirectory()) {
            travel(pathname, callback, function () {
              next(i + 1);
            });
          } else {
            callback(pathname, function () {
              next(i + 1);
            });
          }
        });
      } else {
        finish && finish();
      }
    }(0));
  });

更多关于nodeJS中fs模块对文件及目录进行读写,删除,追加,等操作的相关文章大家可以查看下面的相关链接

广告合作:本站广告合作请联系QQ:858582 申请时备注:广告合作(否则不回)
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!

稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!

昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。

这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。

而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?