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

fs.readFile

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

  1. const fs = require('fs');
  2. fs.readFile('./test.txt', (err, data) => {
  3. if (err) throw err;
  4. console.log(data);
  5. });

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

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

options 可以设置为对象

  1. 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 章节

fs.open

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

  • path 文件路径(实际还能是 Buffer、URL)
  • flags 文件标志位,默认值 r(read 缩写),标识文件用于读取
  • mode 文件模式,默认值 0o666 (rw-)可读写
  • callback

    • err
    • fd 分配到的文件描述符 (file description)

      fs.read

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

demo

  1. 0123456789
  2. abcdefghigklmnopqrstuvwxyz
  3. ABCDEFGHIJKLMNOPQRSTUVWXYZ
  1. const util = require('util')
  2. const fs = require('fs')
  3. const open = util.promisify(fs.open)
  4. const read = util.promisify(fs.read)
  5. const close = util.promisify(fs.close)
  6. const handler = async (url) => {
  7. const fileDescription = await open(url)
  8. const options1 = {
  9. offset: 0,
  10. length: 11
  11. }
  12. const { bytesRead: bytesRead1, buffer: buffer1 } = await read(fileDescription, options1)
  13. console.log(bytesRead1, buffer1.toString()) // 11 01234567890\x00
  14. const options2 = {
  15. offset: 11,
  16. length: 27
  17. }
  18. const { bytesRead: bytesRead2, buffer: buffer2 } = await read(fileDescription, options2)
  19. console.log(bytesRead2, buffer2.toString()) // 27 abcdefghigklmnopqrstuvwxyz\x00
  20. await close(fileDescription)
  21. }
  22. handler('./test.txt')

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 文件

  1. const fs = require('fs');
  2. const rs = fs.createReadStream('./test.txt', { start: 11, end: 36 });
  3. rs.on('open', fd => {
  4. console.log(`文件描述符 ${fd} 已分配`);
  5. });
  6. rs.on('ready', () => {
  7. console.log('文件已准备好');
  8. });
  9. rs.on('data', chunk => {
  10. console.log('读取文件数据:', chunk.toString());
  11. });
  12. rs.on('end', () => {
  13. console.log('文件读取完成');
  14. });
  15. rs.on('close', () => {
  16. console.log('文件已关闭');
  17. });
  18. rs.on('error', (err) => {
  19. console.log('文件读取发生发生异常:', err.stack);
  20. });
  1. 文件描述符 3 已分配
  2. 文件已准备好
  3. 读取文件数据: abcdefghigklmnopqrstuvwxyz
  4. 文件读取完成
  5. 文件已关闭