Node的文件处理涉及到前面说的ptah模块,以及fs文件系统、stream流处理、Buffer缓冲器等模块。内容可能比较多,相关内容请以官网文档为主,此处主要以案例讲解为主,分享给大家一些常用的经典案例。细节就不展开了。

fs文件系统

fs模块提供了很多文件操作相关的api,比如:监控文件夹、文件,文件重命名,文件读写,文件修改权限、文件读写流等。

在此,我们仅以几个案例的方式来驱动学习Node的文件系统,细节请详细阅读Node的api文档或者源码。

案例:

  • 如何监控文件夹的变化?

  • 如何读取一个文件?

  • 如何把内容写入另外一个文件?

  • 文件件读取、文件重命名、移动等各种功能

如何监控文件夹的变化?

fs模块提供了FSWatcher类辅助我们进行监控文件夹,可以通过fs.watch()方法返回此类型实例。然后通过注册相关的事件回调函数达到对文件变化的监控。不废话,直接上代码,详细内容请参考官方文档

// 引入fs模块
const fs = require('fs'); // 通过fs.watch方法可以创建一个fs.FSWatcher类的实例。
let watcher = fs.watch(
__dirname, // 监控的文件夹,我这里用了一个模块的变量,当前js文件所在的目录
{ recursive: true }, // 是否监控子文件夹,还可以设置编码,具体参考官网文档
(eventName, fileName) => { // 回调函数接受两个参数:事件名字和文件名
console.log(`事件名字:${eventName}, 文件名字: ${fileName}`);
}
); // 还可以单独注册事件,回调函数跟watch方法一致。还可以监听:error事件。
watcher.on('change', (eventType, fileName) => {
console.log('事件名:%s , 文件名: %s', eventType, fileName);
}); // 设置13秒中后,退出监控文件夹
setTimeout(() => {
// 关闭监控。
watcher.close(function(err) {
if (err) {
console.error(err);
}
console.log('关闭watch');
});
}, 13000); /********
以下是输出的结果:当js文件修改的时候
事件名字:change, 文件名字: 05filewatch.js
事件名:change , 文件名: 05filewatch.js
事件名字:change, 文件名字: 05filewatch.js
事件名:change , 文件名: 05filewatch.js
*/

如何读取一个文件?

读取文件可以分为小文件读取和大文件,如果小文件可以一次性的把文件内容读取出来然后再处理,对于比较大的文件用文件流处理。

一次性读取小文件的方法

fs.readFile()方法可以帮助我们一次性的把文件里面内容读取出来。文档

const fs = require('fs');
const path = require('path'); // 把当前js所在的目录中的a.html文件的路径赋值给 fileName
let fileName = path.join(__dirname, 'a.html'); // 读取a.html文件,按照utf8的编码方式读取。回调函数第一个参数是err(这个是一个默认的约定规范,大多数node
// 的回调函数第一个参数都是异常的err,如果为空则表示没有错误。)第二个参数是文件的所有内容。
fs.readFile(fileName, { encoding: 'utf8' }, (err, data) => {
if (err) throw err; // 判断是否读取错误
console.log(data); // 文件内容读取并打印到控制台。
}); //readFileSync方法是readFile的同步版本,没有回调函数,函数的返回值就是文件内容。
// 以下代码是同步读取,不使用回调函数,此方法不是用的: libuv 的线程池的线程执行,所以慎用!!
let fileContent = fs.readFileSync(fileName, { encoding: 'utf8' });
console.log(fileContent);

注意:此方法只适合比较小的文件,不适合大文件读取。
同步方法尽量少用,异步的读取文件都是利用了libuv 的线程池的线程读取文件,所以读取文件等待期间不会阻塞主线程的事件循环。

读取大文件

使用stream读取大文件。当然你可以自定义可读流,也可以用node内置的创建可读流的api。

const fs = require('fs');
const path = require('path'); // 把当前js所在的目录中的a.html文件的路径赋值给 fileName
let fileName = path.join(__dirname, 'a.html'); // 创建可读流
let readStream = fs.createReadStream(fileName, {
flags: 'r', // 设置文件只读模式打开文件
encoding: 'utf8' // 设置读取文件的内容的编码
}); // 打开文件流的事件。
readStream.on('open', fd => {
console.log('文件可读流已打开,句柄:%s', fd);
}); // 可读流打开后,会源源不断的触发此事件方法,回调函数参数就是读取的数据。
readStream.on('data', data => {
console.log(data);
}); readStream.on('close', () => {
console.log('文件可读流结束!');
});

如何写入文件

写入文件也是一样的,如果是比较少的内容可以一次性写入文件。其他情况可以用流、管道等方式解决。

一次性写入少量内容到文件

const fs = require('fs');
const path = require('path'); // 把当前js所在的目录中的a.html文件的路径赋值给 fileName
let fileName = path.join(__dirname, 'a.html'); // 写入文件
fs.writeFile(
fileName, // 文件名
'<h1>aicoder.com 全栈实习不8000就业不还实习费</h1>', // 写入文件内容
err => { // 写入成功后的回调函数
if (err) throw err;
console.log('文件内容已经写入!');
}
);

写入大量数据到文件

写入大量文件的方式,可以用流的方式,可以用管道的方式,使用基本类似。且看代码。

  • 流的方式写入文件。

语法: fs.createWriteStream(path, [options])
参数:

  • path 文件路径
  • [options] flags:指定文件操作,默认'w',;encoding,指定读取流编码;start指定写入文件的位置
const fs = require('fs');
const path = require('path'); // 把当前js所在的目录中的a.html文件的路径赋值给 fileName
let fileName = path.join(__dirname, 'msg.log'); // 创建议文件的可写流。
let ws = fs.createWriteStream(fileName, { start: 0 }); ws.on('open', function(fd) {
console.log('要写入的数据文件已经打开,文件描述符是: ' + fd);
}); // 监听写入异常事件
ws.on('error', err => {
console.log(err);
}); // 监听写入完成的事件
ws.on('finish', () => {
console.log('写入结束!');
}); // 写入100个字符串。
for (let i = 0; i < 100; i++) {
// write方法可以把内容写入到可写流中。
let w_flag = ws.write('aicoder.com 全栈实习 \r\n');
//当缓存区写满时,输出false
console.log(w_flag);
} ws.end('结束写入!');
  • 大文件的复制(综合运用文件流)

大文件复制,可以用一个可读流和一个可写流进行复制,由于读取文件一般比写入文件要快。所以要避免缓冲区不可写入的时候,暂停文件流的读取,可以继续写入的时候,再继续读取数据。

const fs = require('fs');
const path = require('path'); let fileNameSrc = path.join(__dirname, 'jdk.dmg'); // 复制的源文件
let fileNameDist = path.join(__dirname, 'jdk1.dmg'); // 拷贝完的目标文件名 // 创建可读流
let rs = fs.createReadStream(fileNameSrc, { start: 0 });
// 创建可写流
let ws = fs.createWriteStream(fileNameDist, { start: 0 }); rs.on('data', function(chunk) {
if (ws.write(chunk) === false) {
// 判断数据流是否已经写入目标了
rs.pause();
}
}); // 当可读流结束的时候,让可写流结束。
rs.on('end', function() {
ws.end(); // 结束可写流
console.log('文件复制成功');
}); ws.on('drain', function() {
rs.resume(); // 继续启动读取数据流
});
  • 使用管道的方式大文件的复制。
const fs = require('fs');
const path = require('path'); let fileNameSrc = path.join(__dirname, 'jdk.dmg'); // 复制的源文件
let fileNameDist = path.join(__dirname, 'jdk1.dmg'); // 拷贝完的目标文件名 let rs = fs.createReadStream(fileNameSrc, { start: 0 });
let ws = fs.createWriteStream(fileNameDist, { start: 0 }); rs.on('end', () => {
console.log('读取完毕');
});
ws.on('finish', () => {
console.log('写入成功!');
}); // 可读流直接跟可写流建立管道。可读流的数据 直接通过管道流向 可写流
rs.pipe(ws);
/******************* |----------|
|----------|
|---rs ---|-----------\
|----------| |
|__________| |
|
| | |
| | |
|----------|
| ws |
|__________| ********************/

文件相关的其他操作

重命名

语法:fs.rename(oldPath, newPath, callback);
参数:

  • oldPath, 原目录/文件的完整路径及名;
  • newPath, 新目录/文件的完整路径及名;如果新路径与原路径相同,而只文件名不同,则是重命名
  • [callback(err)], 操作完成回调函数;err操作失败对象
fs.rename(__dirname + '/test', __dirname + '/fsDir', function (err) {
if(err) {
console.error(err);
return;
}
console.log('重命名成功')
});

修改文件或目录的操作权限

语法:fs.utimes(path, mode, callback);
参数:

  • path, 要查看目录/文件的完整路径及名;
  • mode, 指定权限,如:0666 8进制,权限:所有用户可读、写,
  • [callback(err)], 操作完成回调函数;err操作失败对象
fs.chmod(__dirname + '/fsDir', 0666, function (err) {
if(err) {
console.error(err);
return;
}
console.log('修改权限成功')
});

查看文件与目录的是否存在

语法:fs.exists(path, callback);
参数:

  • path, 要查看目录/文件的完整路径及名;
  • [callback(exists)], 操作完成回调函数;exists true存在,false表示不存在
fs.exists(__dirname + '/te', function (exists) {
var retTxt = exists ? retTxt = '文件存在' : '文件不存在';
console.log(retTxt);
});

文件夹读取操作

const fs = require('fs');
const path = require('path'); // 读取文件夹
fs.readdir(__dirname, function(err, files) {
if (err) {
console.error(err);
return;
}
// 对文件夹中的所有路径做处理
files.forEach(file => {
let filePath = path.join(__dirname, file);
// 读取文件的信息,判断是文件还是路径
fs.stat(filePath, function(err, stat) {
if (err) throw err;
console.log(filePath, stat.isFile() ? ' is: file' : ' is: dir');
});
});
});

后续的学习

后续,可以实现一下自定义的可写流、可读流,多运用一些管道的方法,多看一下官方的文档,相信您已经可以掌握了文件模块相关的内容。


参考:

  1. node.js之fs模块
  2. Node.js 文件系统fs模块

老马免费视频教程

返回教程列表首页

github地址:https://github.com/malun666/aicoder_node

 

Node入门教程(9)第七章:NodeJs的文件处理的更多相关文章

  1. ArcGIS for Desktop入门教程_第七章_使用ArcGIS进行空间分析 - ArcGIS知乎-新一代ArcGIS问答社区

    原文:ArcGIS for Desktop入门教程_第七章_使用ArcGIS进行空间分析 - ArcGIS知乎-新一代ArcGIS问答社区 1 使用ArcGIS进行空间分析 1.1 GIS分析基础 G ...

  2. D3.js的v5版本入门教程(第七章)—— 比例尺的使用

    D3.js的v5版本入门教程(第七章) 比例尺在D3.js中是一个很重要的东西,我们可以这样理解d3.js中的比例尺——一种映射关系,从domain映射到range域(为什么会是domain和rang ...

  3. Node入门教程(4)第三章:第一个 Nodejs 程序

    第一个 Nodejs 程序 本教程仅适合您已经有一定的JS编程的基础或者是后端语言开发的基础.如果您是零基础,建议您先学一下老马的前端免费视频教程 第一步:创建项目文件夹 首先创建 demos 文件夹 ...

  4. Node入门教程(6)第五章:node 模块化(上)模块化演进

    node 模块化 JS 诞生的时候,仅仅是为了实现网页表单的本地校验和简单的 dom 操作处理.所以并没有模块化的规范设计. 项目小的时候,我们可以通过命名空间.局部作用域.自执行函数等手段实现变量不 ...

  5. Node入门教程(7)第五章:node 模块化(下) npm与yarn详解

    Node的包管理器 JavaScript缺少包结构的定义,而CommonJS定义了一系列的规范.而NPM的出现则是为了在CommonJS规范的基础上,实现解决包的安装卸载,依赖管理,版本管理等问题. ...

  6. Node入门教程(5)第四章:global 全局变量

    global - 全局变量 全局对象(global object),不要和 全局的对象( global objects )或称标准内置对象混淆.这里说的全局的对象是说在全局作用域里的内的对象.全局作用 ...

  7. Node入门教程(13)第十一章:mocha单元测试+should断言库+istanbul覆盖率测试+art-template

    声明:以下为老马的全栈视频教程的笔记,如果需要了解详情,请直接配合视频学习.视频全部免费,视频地址:https://ke.qq.com/course/294595?tuin=1eb4a0a4 node ...

  8. Node入门教程(8)第六章:path 模块详解

    path 模块详解 path 模块提供了一些工具函数,用于处理文件与目录的路径.由于windows和其他系统之间路径不统一,path模块还专门做了相关处理,屏蔽了彼此之间的差异. 可移植操作系统接口( ...

  9. ArcGIS for Desktop入门教程_第五章_ArcCatalog使用 - ArcGIS知乎-新一代ArcGIS问答社区

    原文:ArcGIS for Desktop入门教程_第五章_ArcCatalog使用 - ArcGIS知乎-新一代ArcGIS问答社区 1 ArcCatalog使用 1.1 GIS数据 地理信息系统, ...

随机推荐

  1. 理解 static (深入了解JAVA虚拟机)

    谈谈我对static的理解 因为我发现很多同学学到这里都会很困惑 很难理解static到底是个什么 首先 static是个修饰符 被static修饰的变量我们统称为静态变量也叫类变量(为什么叫类变量呢 ...

  2. IE8 margin:0 auto 不能居中显示的问题

    ie8下面margin:0 auto;不能居中的解决方案,ie8兼容性代码 今天写了个div,用margin:0 auto:来定义他的属性,让他居中,结果,竟然无效. 一开始以为是css里的代码冲突了 ...

  3. HDU.3516.Tree Construction(DP 四边形不等式)

    题目链接 贴个教程: 四边形不等式学习笔记 \(Description\) 给出平面上的\(n\)个点,满足\(X_i\)严格单增,\(Y_i\)严格单减.以\(x\)轴和\(y\)轴正方向作边,使这 ...

  4. (58)Wangdao.com第九天_JavaScript 对象的基本操作

    对象的基本操作 创建对象 var 对象名 = new Object();        // new 函数;    称为构造函数,专门用来创建对象的函数 var god =  给对象增加属性 删除对象 ...

  5. 用Jersey构建RESTful服务1--HelloWorld

    一.环境1.Eclipse Juno R22. Tomcat 73. Jersey 2.7  下载地址( https://jersey.java.net/download.html) 二.流程1.Ec ...

  6. AngularJS中监视Scope变量以及外部调用Scope方法

    在AngularJS中,有时候需要监视Scope中的某个变量,因为变量的改变会影响一些界面元素的显示.有时,也希望通过jQuery调用Scope的某个方法. 比如以下场景: <div> & ...

  7. ArcGIS教程:曲率

    摘要 计算栅格表面的曲率,包括剖面曲率和平面曲率. 用法 · 主要输出结果为每个像元的表面曲率,该值通过将该像元与八个相邻像元拟合而得.曲率是表面的二阶导数,或者可称之为坡度的坡度.可供选择的输出曲率 ...

  8. CSS3 Flex布局整理(三)-项目属性

    一.Flex布局中 Flex Item属性控制,可以指定显示顺序.剩余空间的放大,缩小.交叉轴的排列 1.order:定义项目的排列顺序,数值越小,排列越靠前,默认为0.类似z-index 2.fle ...

  9. WinPcap权威指南(一)

    WinPcap是一个开源的网络抓包模块,顾名思义,它只能工作在Windows下,但本文介绍的知识并不局限于任何操作系统和开发语言,因为网络协议本身是没有这些区别的.阅读本指南之前,请先下载WinPca ...

  10. (转) Java RandomAccessFile与MappedByteBuffer

    RandomAccessFile RandomAccessFile是用来访问那些保存数据记录的文件的,你就可以用seek( )方法来访问记录,并进行读写了.这些记录的大小不必相同:但是其大小和位置必须 ...