fs模块 - 使用文件流进行文件的读取

197 阅读2分钟

fs模块

在nodejs中fs模块是进行文件的io操作的一个类,在这个模块下包含很多操作文件的api,具体内容直接去官网看API即可,接下来将讲解使用fs模块进行文件流的读取和写入操作

预备知识

首先在进行文件流的读写案例之前,我们需要知道几个常用的path的api:

  • path.join() 路径拼接的
  • path.resolve()也是路径拼接的,和path.join的唯一区别就是path.resolve是通过类似于cd的形式进行路径的拼接的
  • __dirname 表示当前文件所在的目录的绝对路径(不包含当前执行的文件名)
  • __filename 表示当前文件的绝对路径(包含当前执行文件名)

fs相关API

在这个案例中,我们需要提前知道一下几个API的用法:

  • fs.createReadStream

    fs.createReadStream是创建文件可读流的一个API

    参数1:string | buffer | url

    参数2:options对象

const fs = require('fs');
const path = require('path');

// 源文件路径和目标文件路径
const sourceFilePath = path.join(__dirname, 'NN.pdf');

// 创建可读流
const readStream = fs.createReadStream(sourceFilePath);

// 监听 'data' 事件以更新进度
readStream.on('data', chunk => {
  transferredBytes += chunk.length;
  const percentTransferred = (transferredBytes / totalBytes) * 100;

  process.stdout.clearLine(); // 清除当前行
  process.stdout.cursorTo(0); // 将光标移动到行首
  process.stdout.write(`传输进度: ${percentTransferred.toFixed(2)}%`);
});

// 错误处理
readStream.on('error', err => console.error('读取文件时出错:', err));

// 使用管道连接两个流
readStream.pipe(writeStream);
  • fs.createWriteStream

    fs.createWriteStream 是创建可写流的一个API

    参数:和可读流参数一致

const fs = require('fs');
const path = require('path');

// 源文件路径和目标文件路径
const destinationFilePath = path.join(__dirname, 'destination-large-file.txt');

// 创建可读流和可写流
const writeStream = fs.createWriteStream(destinationFilePath);

// 当所有数据被写入后触发
writeStream.on('finish', () => {
  console.log('\n文件传输成功完成!');
});

// 错误处理
writeStream.on('error', err => console.error('写入文件时出错:', err));

// 使用管道连接两个流
readStream.pipe(writeStream);
  • fs.stat

    fs.stat 是一个获取文件信息的API。该方法并不会读取整个文件内容,而是查询文件系统的元数据来获取文件信息。文件系统会为每个文件维护一些元数据,其中包括文件大小、创建时间、修改时间等信息fs.stat() 只是请求这些已存在于文件系统中的元数据,因此它是一个非常快速和轻量级的操作,不会导致大量磁盘I/O。

const fs = require('fs');
const path = require('path');

// 源文件路径和目标文件路径
const sourceFilePath = path.join(__dirname, 'NN.pdf');

// 获取文件总大小
fs.stat(sourceFilePath, (err, stats) => {
  if (err) throw err;

  let totalBytes = stats.size;

});

其中stats参数是一个对象:
stats {
  dev: 2114,
  ino: 48064969,
  mode: 33188,
  nlink: 1,
  uid: 85,
  gid: 100,
  rdev: 0,
  size: 527,  // 主要是这个参数,返回的是文件的字节大小
  blksize: 4096,
  blocks: 8,
  atimeMs: 1318289051000.1,
  mtimeMs: 1318289051000.1,
  ctimeMs: 1318289051000.1,
  birthtimeMs: 1318289051000.1,
  atime: Mon, 10 Oct 2011 23:24:11 GMT,
  mtime: Mon, 10 Oct 2011 23:24:11 GMT,
  ctime: Mon, 10 Oct 2011 23:24:11 GMT,
  birthtime: Mon, 10 Oct 2011 23:24:11 GMT } 
  • buffer对象
    • Buffer对象是Node处理二进制数据的一个接口。它是Node原生提供的全局对象,可以直接使用,不需要require('buffer')
    • length 属性:length属性返回Buffer对象所占据的内存长度,返回值是xxx字节。

文件流案例

普通的文件流读取

const fs = require('fs');
const path = require('path');

// 源文件路径和目标文件路径
const sourceFilePath = path.join(__dirname, 'NN.pdf');
const destinationFilePath = path.join(__dirname, 'destination-large-file.txt');

// 创建可读流和可写流
const readStream = fs.createReadStream(sourceFilePath);
const writeStream = fs.createWriteStream(destinationFilePath);

// 获取文件总大小
fs.stat(sourceFilePath, (err, stats) => {
  if (err) throw err;

  let totalBytes = stats.size;
  let transferredBytes = 0;

  // 监听 'data' 事件以更新进度
  readStream.on('data', chunk => {
    // chunk是一个 buffer对象 
    transferredBytes += chunk.length;
    const percentTransferred = (transferredBytes / totalBytes) * 100;

    process.stdout.clearLine(); // 清除当前行
    process.stdout.cursorTo(0); // 将光标移动到行首
    process.stdout.write(`传输进度: ${percentTransferred.toFixed(2)}%`);
  });

  // 当所有数据被写入后触发
  writeStream.on('finish', () => {
    console.log('\n文件传输成功完成!');
  });

  // 错误处理
  readStream.on('error', err => console.error('读取文件时出错:', err));
  writeStream.on('error', err => console.error('写入文件时出错:', err));

  // 使用管道连接两个流
  readStream.pipe(writeStream);
});

在这个代码中,监听 'data' 事件以更新进度我们使用的是简单的展示效果。我们可以使用一个第三方库在控制台进行文件流进度展示,那这就需要使用cli-progress来进行操作啦。

使用cli-progress进行文件流进度可视化

/**
 * 进阶版 :使用cli-progress进行可视化展示
 */

const fs = require('fs');
const path = require('path');
const cliProgress = require('cli-progress');

// create a new progress bar instance and use shades_classic theme
const bar1 = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);

// 源文件路径和目标文件路径
const sourceFilePath = path.join(__dirname, 'NN.pdf');
const destinationFilePath = path.join(__dirname, 'destination-large-file.txt');

// 创建可读流和可写流
const readStream = fs.createReadStream(sourceFilePath);
const writeStream = fs.createWriteStream(destinationFilePath);

// 获取文件总大小
fs.stat(sourceFilePath, (err, stats) => {
  if (err) throw err;

  // 使用管道连接两个流
  readStream.pipe(writeStream);

  let totalBytes = stats.size;
  let transferredBytes = 0;
  bar1.start(totalBytes, 0);

  // 监听 'data' 事件以更新进度
  readStream.on('data', chunk => {
    transferredBytes += chunk.length;
    bar1.update(transferredBytes);
    if (transferredBytes >= totalBytes) {
      bar1.stop();
    }
  });

  // 当所有数据被写入后触发
  writeStream.on('finish', () => {
    console.log('\n文件传输成功完成!');
  });

  // 错误处理
  readStream.on('error', err => console.error('读取文件时出错:', err));
  writeStream.on('error', err => console.error('写入文件时出错:', err));
});

最后实现的效果如下:

image.png

引用

node 操作文件流 fs 同步与异步 流式文件的写入与读取

深入解析fs.ReadStream:Node.js中的文件读取流利器

Node fs模块