【问题标题】:How to run an async function for each line of a very large (> 1GB) file in Node.js如何在 Node.js 中为非常大(> 1GB)文件的每一行运行异步函数
【发布时间】:2012-06-20 03:28:47
【问题描述】:

假设您有一个巨大的 (> 1GB) CSV 记录 ID:

655453
4930285
493029
4930301
493031
...

对于每个id,您都希望调用 REST API 来获取记录数据,在本地对其进行转换,然后将其插入到本地数据库中。

您如何使用 Node.js 的 Readable Stream 做到这一点?

我的问题基本上是这样的:你如何逐行读取一个非常大的文件,为每一行运行一个异步函数,并且[可选地]能够从特定行开始读取文件?

从以下 Quora 问题我开始学习使用fs.createReadStream

http://www.quora.com/What-is-the-best-way-to-read-a-file-line-by-line-in-node-js

var fs = require('fs');
var lazy = require('lazy');

var stream = fs.createReadStream(path, {
  flags: 'r',
  encoding: 'utf-8'
});

new lazy(stream).lines.forEach(function(line) {
  var id = line.toString();
  // pause stream
  stream.pause();
  // make async API call...
  makeAPICall(id, function() {
    // then resume to process next id
    stream.resume();
  });
});

但是,该伪代码不起作用,因为lazy module 强制您读取整个文件(作为流,但没有暂停)。所以这种方法似乎行不通。

另一件事是,我希望能够从特定行开始处理此文件。这样做的原因是,处理每个id(进行 api 调用、清理数据等)每条记录最多可能需要半秒,所以我不想从文件的开头开始每一次。我正在考虑使用的天真的方法是只捕获最后处理的 id 的行号,然后保存它。然后,当您再次解析文件时,您逐行遍历所有 id,直到找到您离开的行号,然后您执行makeAPICall 业务。另一种天真的方法是编写小文件(比如 100 个 id)并一次处理每个文件(足够小的数据集,可以在没有 IO 流的情况下在内存中执行所有操作)。有没有更好的方法来做到这一点?

我可以看到这是如何变得棘手(以及 node-lazy 的来源),因为 stream.on('data', function(chunk) {}); 中的 chunk 可能只包含一行的 部分(如果 bufferSize 很小,每个块可能是 10 行,但因为 id 是可变长度的,它可能只有 9.5 行或其他)。这就是为什么我想知道解决上述问题的最佳方法是什么。

【问题讨论】:

标签: javascript node.js asynchronous stream filestream


【解决方案1】:

我猜你不需要使用node-lazy。这是我在Node docs 中找到的:

事件data

function (data) { }

data 事件发出 Buffer(默认情况下)或 string 如果 使用了setEncoding()

这意味着您在流上调用setEncoding(),然后您的data 事件回调将接受字符串参数。然后在这个回调中你可以调用使用.pause().resume() 方法。

伪代码应如下所示:

stream.setEncoding('utf8');
stream.addListener('data', function (line) {
    // pause stream
    stream.pause();
    // make async API call...
    makeAPICall(line, function() {
        // then resume to process next line
        stream.resume();
    });
})

虽然文档没有明确指定流是逐行读取的,但我认为文件流就是这种情况。至少在其他语言和平台中,文本流以这种方式工作,我认为节点流没有任何不同的理由。

【讨论】:

  • 流不是行缓冲的,它会为您提供一大块可能以换行符结束的数据。
【解决方案2】:

与 Andrew Андрей Листочкин 的回答有关:

您可以使用byline 之类的模块为每一行获取单独的data 事件。它是围绕原始文件流的转换流,它为每个块生成一个data 事件。这使您可以在每一行之后暂停。

byline 不会像lazy 那样将整个文件读入内存。

var fs = require('fs');
var byline = require('byline');

var stream = fs.createReadStream('bigFile.txt');
stream.setEncoding('utf8');

// Comment out this line to see what the transform stream changes.
stream = byline.createStream(stream); 

// Write each line to the console with a delay.
stream.on('data', function(line) {
  // Pause until we're done processing this line.
  stream.pause();

  setTimeout(() => {
      console.log(line);

      // Resume processing.
      stream.resume();
  }, 200);
});

【讨论】:

    猜你喜欢
    • 2012-06-20
    • 2022-11-16
    • 2018-01-16
    • 2015-11-12
    • 2021-03-29
    • 1970-01-01
    • 1970-01-01
    • 2018-11-21
    • 1970-01-01
    相关资源
    最近更新 更多