极简 Node.js 入门系列教程:https://www.yuque.com/sunluyong/node

本文更佳阅读体验:https://www.yuque.com/sunluyong/node/fs-read

Node.js 提供了多种读取文件的 API

fs.readFile

fs.readFile(path[, options], callback) 是最常用的读取文件方法,用于异步读取文件的全部内容

const fs = require('fs');

fs.readFile('./test.txt', (err, data) => {
if (err) throw err;
console.log(data);
});

回调会传入两个参数 (err, data),其中 data 是文件的内容,如果 options 是字符串,则它指定字符编码:

fs.readFile('./test.txt', 'utf8', callback);

options 可以设置为对象

fs.readFile('./test.txt', { encoding: 'utf8', flag: 'r' }, callback);

fs.open、fs.read、fs.close

fs.readFile 使用相当简单,在大部分读取小文件的时候我们都应该使用这个方法,但 fs.readFile() 会把文件全部内容读取,如果想精确读取部分文件内容,Node.js 也提供了类似 C 语言 fopen、fgetc、fclose 的操作

在 Node.js 中读取一个文件同样有三步

  1. fs.open():分配(打开)文件描述符
  2. fs.read():读取文件内容
  3. fs.close():关闭文件描述符

> 在 POSIX 每个打开的文件系统都分配了一个称为文件描述符的数字。 文件系统操作使用文件描述符来标识和跟踪每个特定的文件。一旦被分配,则文件描述符可用于从文件读取数据、向文件写入数据、或请求关于文件的信息
> buffer 相关知识需要先行查看 > [Buffer](https://www.yuque.com/sunluyong/node/buffer) 章节

### fs.open(path[, flags[, mode]], callback)
通过 fs.open 方法可以打开一个文件,获取分配到的文件描述符,方法参数含义:

  • path 文件路径(实际还能是 Buffer、URL)
  • flags 文件标志位,默认值 r(read 缩写),标识文件用于读取
  • mode 文件模式,默认值 0o666 (rw-)可读写
  • callback
    • err
    • fd 分配到的文件描述符 (file description)

fs.read(fd, [options,] callback)

fs.read 用于从文件描述符中读取数据,方法参数含义:

  • fd 文件描述符
  • options 可选项,不设置使用下述默认值
    • buffer:数据(从 fd 读取)将被写入的缓冲区,默认会申请一个新的缓冲区 Buffer.alloc(16384)
    • offset:buffer 开始写入的偏移量,默认值为 0
    • length:需要读取的字节数,默认使用 buffer.length
    • position:从文件中开始读取数据的位置;如果 position 为 null,则从当前文件位置读取,并重置文件位置到上次位置;如果 position 是整数,则文件位置会被保持。默认值为 null
  • callback
    • err
    • bytesRead
    • buffer

fs.read 还有一个需要把参数写全的重载 fs.read(fd, buffer, offset, length, position, callback)

fs.close(fs, callback)

fs.close 用于关闭文件描述符,大多数操作系统都会限制同时打开的文件描述符数量,因此当操作完成时关闭描述符非常重要。 如果不这样做将导致内存泄漏,最终导致应用程序崩溃

demo

test.txt

0123456789
abcdefghigklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
const fs = require('fs');
const promisify = require('util').promisify; const open = promisify(fs.open);
const read = promisify(fs.read);
const close = promisify(fs.close); async function test() {
const fd = await open('./test.txt');
const readOptions = {
// buffer: Buffer.alloc(26), 异步调用默认可以不设置,如果希望读取的字节写入指定的缓冲区可以指定
position: 11, // 从第 11 个字节开始读取,读取后文件位置被重置
length: 26, // 读取 26 个字节
}; const { bytesRead: bytesRead1, buffer: buf1 } = await read(fd, readOptions); console.log(`第一次读取字节数: ${bytesRead1}`);
console.log(`第一次读取数据内容: ${buf1.toString()}`); // 不指定 position,文件位置每次读取后会保持
const { bytesRead: bytesRead2, buffer: buf2 } = await read(fd, { length: 1 });
console.log(`第二次从文件重置后位置读取 ${bytesRead2} 字节内容: ${buf2.toString()}`); const { bytesRead: bytesRead3, buffer: buf3 } = await read(fd, { length: 1 });
console.log(`第三次从文件当前位置读取 ${bytesRead3} 字节内容: ${buf3.toString()}`); await close(fd);
console.log(`文件描述符 ${fd} 已关闭`);
} test();
第一次读取字节数: 26
第一次读取数据内容: abcdefghigklmnopqrstuvwxyz
第二次从文件重置后位置读取 1 字节内容: 0
第三次从文件当前位置读取 1 字节内容: 1
文件描述符 20 已关闭
  • 程序第一次从第 11 个字节开始读取 test.txt 内容,一共读取 26 个字节
  • 第一次读取设置了 position 属性,读取完成后文件指针位置被重置为 0
  • 第二次读取没有设置 position 读取一个字节后,文件位置停留在 1
  • 第三次读取直接从文件位置 1 开始读取

除非希望精确控制,否则不要使用这种方式读取文件,手工控制缓冲区、文件位置指针很容易出现各种意外状况

fs.createReadStream

对于大文件读取一般使用流的方式,关于流的简单原理在后面章节有专门介绍,本章介绍一下使用 fs 创建可读文件流
fs.createReadStream(path[, options])

  1. path
  2. options(比较常用的有)
    • fd: 如果指定了 fd,则 ReadStream 会忽略 path 参数,使用指定的文件描述符(不会再次触发 open 事件)
    • autoClose: 默认值: true,文件读取完成或者出现异常时是否自动关闭文件描述符
    • start: 开始读取位置
    • end: 结束读取位置
    • highWaterMark: 默认值: 64 * 1024,普通可读流一般是 16k

流的各个状态会有对应的事件抛出,还是读取上文用过的 test.txt 文件

const fs = require('fs');

const rs = fs.createReadStream('./test.txt', { start: 11, end: 36 });

rs.on('open', fd => {
console.log(`文件描述符 ${fd} 已分配`);
}); rs.on('ready', () => {
console.log('文件已准备好');
}); rs.on('data', chunk => {
console.log('读取文件数据:', chunk.toString());
}); rs.on('end', () => {
console.log('文件读取完成');
}); rs.on('close', () => {
console.log('文件已关闭');
}); rs.on('error', (err) => {
console.log('文件读取发生发生异常:', err.stack);
});
文件描述符 20 已分配
文件已准备好
读取文件数据: abcdefghigklmnopqrstuvwxyz
文件读取完成
文件已关闭

可读流详细操作参考:可读流 https://www.yuque.com/sunluyong/node/readable

极简 Node.js 入门 - 3.2 文件读取的更多相关文章

  1. 极简 Node.js 入门 - 3.3 文件写入

    极简 Node.js 入门系列教程:https://www.yuque.com/sunluyong/node 本文更佳阅读体验:https://www.yuque.com/sunluyong/node ...

  2. 极简 Node.js 入门 - 3.4 文件夹写入

    极简 Node.js 入门系列教程:https://www.yuque.com/sunluyong/node 本文更佳阅读体验:https://www.yuque.com/sunluyong/node ...

  3. 极简 Node.js 入门 - 3.5 文件夹操作

    极简 Node.js 入门系列教程:https://www.yuque.com/sunluyong/node 本文更佳阅读体验:https://www.yuque.com/sunluyong/node ...

  4. 极简 Node.js 入门 - 1.2 模块系统

    极简 Node.js 入门系列教程:https://www.yuque.com/sunluyong/node 本文更佳阅读体验:https://www.yuque.com/sunluyong/node ...

  5. 极简 Node.js 入门 - 1.3 调试

    极简 Node.js 入门系列教程:https://www.yuque.com/sunluyong/node 本文更佳阅读体验:https://www.yuque.com/sunluyong/node ...

  6. 极简 Node.js 入门 - 1.4 NPM & package.json

    极简 Node.js 入门系列教程:https://www.yuque.com/sunluyong/node 本文更佳阅读体验:https://www.yuque.com/sunluyong/node ...

  7. 极简 Node.js 入门 - 2.1 Path

    极简 Node.js 入门系列教程:https://www.yuque.com/sunluyong/node 本文更佳阅读体验:https://www.yuque.com/sunluyong/node ...

  8. 极简 Node.js 入门 - 2.3 process

    极简 Node.js 入门系列教程:https://www.yuque.com/sunluyong/node 本文更佳阅读体验:https://www.yuque.com/sunluyong/node ...

  9. 极简 Node.js 入门 - 2.4 定时器

    极简 Node.js 入门系列教程:https://www.yuque.com/sunluyong/node 本文更佳阅读体验:https://www.yuque.com/sunluyong/node ...

随机推荐

  1. Java基础之NIO

    NIO简介: Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API.NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同 ...

  2. ElasticSearch(一)概念介绍及环境搭建

    一.什么是ElasticSearch: Elasticsearch (ES)是一个基于Lucene构建的开源.分布式.RESTful 接口全文搜索引擎.Elasticsearch 还是一个分布式文档数 ...

  3. 面试题六十:n个骰子的点数

    把n个骰子扔在地上,求出现和为s的概率 可得n<=s<=6n 方法:定义6n-n+1长度的数组,然后对所有可能出现的组合进行计算,把结果进行计数存进数组:递归 方法二:动态规划,大问题小化 ...

  4. HTTP Keep-Alive的作用

    作用: Keep-Alive:使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接.Web服务器,基本上都支持HTTP Keep-Alive ...

  5. HTML自动刷新页面

    <meta http-equiv="refresh"content="5"/> 英文""

  6. Explain关键字解析

    Explain 用法 explain模拟Mysql优化器是如何执行SQL查询语句的,从而知道Mysql是如何处理你的SQL语句的.分析你的查询语句或是表结构的性能瓶颈. 语法:Explain + SQ ...

  7. c++ 第二天 命名空间、数组

    C++ 命名空间 命名空间,也就是名称空间/名字空间,注意需要的头文件是 iostream ,而不是 iostream.h ,后者是旧版本的 C++ 头文件,并不支持命名空间. 为什么要使用命名空间? ...

  8. 一本通 高手训练 1782 分层图 状压dp

    LINK:分层图 很精辟的一道题 写的时候没带脑子 导致搞了半天不知道哪错了. 可以想到状压每次到某一层的状态 然后这个表示方案数 多开一维表示此时路径条数的奇偶即可. 不过显然我们只需要知道路径条数 ...

  9. CF 633 div1 1338 B. Edge Weight Assignment 构造

    LINK:Edge Weight Assignment 这场当时没打 看到这个B题吓到我了 还好当时没打. 想了20min才知道怎么做 而且还不能证明. 首先考虑求最小. 可以发现 如果任意两个叶子节 ...

  10. 埋在MySQL数据库应用中的17个关键问题!

    作者:扎瓦陈序元 来源:https://blog.csdn.net/weixin_42882439 MySQL的使用非常普遍,跟MySQL有关的话题也非常多,如性能优化.高可用性.强一致性.安全.备份 ...