学习笔记:使用nodejs中的stream读取大文件内容

2,488 阅读1分钟

背景

最近业务发展迅速,运营表示需要制作sitemap优化seo,于是后端丢给我一个有几千万条数据的txt文件,让我自己根据id去拼接,最终会将多个符合sitemap标准的xml文件交付给运营。

txt文件格式如下:

123
456
789
xxx
yyy

思路

最开始的思路,是使用fs模块中readFileSync和appendFileSync这两个api,将读到的内容按行分割,然后使用模版字符串拼接,核心代码如下

const fs = require("fs");

fs.readFile("./validBrandItems.txt", "utf-8", (err, data) => {
  if (err) {
    console.log('err => ', err);
  } else {
    // todo...
  }
});

用这种方式本没有大问题,但坏就坏在,这个txt文件体积太大,我用nodejs读取直接给我报错,出现这个error的原因是文件的体积太大了,node会做限制。

caught err Error: Cannot create a string longer than 0x1fffffe8 characters
    at Object.slice (buffer.js:608:37)
    at Buffer.toString (buffer.js:805:14)
    at FSReqCallback.readFileAfterClose [as oncomplete] (internal/fs/read_file_context.js:58:23) {
  code: 'ERR_STRING_TOO_LONG'
}

解决方案

在我一通百度谷歌之后,发现这种情况需要使用stream来读取,简单来说就是可以避免一下子把整个文件全都读进内存,可以一点一点加载文件,在流媒体网站中常用这种方式传输数据。

在读取文件时主要用了fs模块的createReadStream和readline模块的createInterface这两个api,前者可以创建一个可读流,后者可以按行(line by line)读取文件,而写文件我依然使用appendFileSync这个api。

const fs = require("fs");
const readline = require("readline");

const source = "./validBrandItems.txt"; // 读取目标
const rs = fs.createReadStream(source);

const rl = readline.createInterface({
  input: rs,
  crlfDelay: Infinity,
});

rl.on("line", (line) => {
  if (!line) return;
  
  // todo...
}

通过以上代码基本解决了在读取大文件时node限制的问题,以及学到了stream的实际应用场景。