Node.JS中的createReadStream

时间:2015-06-02 16:00:17

标签: javascript node.js file stream readfile

所以我使用了fs.readFile(),它给了我

  

"致命错误:CALL_AND_RETRY_LAST分配失败 - 处理完毕   存储器"

因为fs.readFile()在调用回调之前将整个文件加载到内存中,我应该使用fs.createReadStream()吗?

这是我以前用readFile做的事情:

fs.readFile('myfile.json', function (err1, data) {
    if (err1) {
        console.error(err1);
    } else {
        var myData = JSON.parse(data);
        //Do some operation on myData here
    }
}

对不起,我对流媒体有点新意;以下是使用流媒体做同样事情的正确方法吗?

var readStream = fs.createReadStream('myfile.json');

readStream.on('end', function () {  
    readStream.close();
    var myData = JSON.parse(readStream);
    //Do some operation on myData here
});

由于

1 个答案:

答案 0 :(得分:15)

如果文件很大,那么是的,流媒体将是您想要处理它的方式。但是,您在第二个示例中所做的是让流将所有文件数据缓冲到内存中,然后在end上处理它。它与readFile的方式基本没有区别。

您想要查看JSONStream。流式传输意味着您希望在流过时处理数据。在你的情况下,你显然来执行此操作,因为你无法一次性将整个文件缓冲到内存中。考虑到这一点,希望这样的代码是有道理的:

JSONStream.parse('rows.*.doc')

请注意,它有一种查询模式。那是因为你不会让文件中的整个JSON对象/数组同时使用所有JSON对象/数组,所以你必须更多地考虑你希望JSONStream如何处理数据

您可以使用JSONStream来实质查询您感兴趣的JSON数据。这样您就不会将整个文件缓冲到内存中。它确实有缺点,如果你确实需要所有的数据,那么你必须多次流式传输文件,使用JSONStream只提取你当时所需的数据,但在你的情况下,你不要#&# 39;有很多选择。

您还可以使用JSONStream按顺序解析数据,并执行将其转储到数据库中的操作。

JSONStream.parseJSON.parse类似,但它不返回整个对象,而是返回一个流。当解析流获取足够的数据以形成与查询匹配的整个对象时,它将发出data事件,其中数据是与您的查询匹配的文档。一旦配置了数据处理程序,您就可以将读取的数据流传输到解析流中,并观察魔术的发生。

示例:

var JSONStream = require('JSONStream');
var readStream = fs.createReadStream('myfile.json');
var parseStream = JSONStream.parse('rows.*.doc');
parseStream.on('data', function (doc) {
  db.insert(doc); // pseudo-code for inserting doc into a pretend database.
});
readStream.pipe(parseStream);

这是帮助您了解正在发生的事情的冗长方式。这是一种更简洁的方式:

var JSONStream = require('JSONStream');
fs.createReadStream('myfile.json')
  .pipe(JSONStream.parse('rows.*.doc'))
  .on('data', function (doc) {
    db.insert(doc);
  });

编辑:

为了进一步明确发生了什么,请尝试这样考虑。让我们假设你有一个巨大的湖泊,你想要对水进行净化并将水移到新的水库。如果你有一个巨大的神奇直升机和一个巨大的水桶,那么你可以飞越湖面,将湖泊放入水桶,加入处理化学品,然后飞到目的地。

问题当然是没有这样的直升机可以处理那么多的重量或体积。这根本不可能,但这并不意味着我们不能以不同的方式实现我们的目标。因此,您将在湖泊和新水库之间建造一系列河流(溪流)。然后,您可以在这些河流中设置净化站,净化通过它的任何水。这些站可以以各种方式操作。也许治疗可以做得如此之快,以至于你可以让河流自由流动,当水以最大速度沿着河流流动时,净化就会发生。

也可能需要一些时间来处理水,或者该站需要一定量的水才能有效地处理它。所以你设计你的河流有闸门,你控制从湖里流入你的河流的水流,让站点缓冲他们需要的水,直到他们完成他们的工作并释放下游和下游的净化水它的最终目的地。

这几乎就是您想要处理的数据。解析流是您的清理工作站,它会缓冲数据,直到它足以形成与您的查询匹配的整个文档,然后它将该数据推送到下游(并发出data事件)。

节点流很好,因为大多数时候你不必处理打开和关闭门。当流缓冲一定量的数据时,节点流足够智能以控制回流。好像清洁站和湖上的大门正在互相交谈,以确定完美的流速。

如果您有流式数据库驱动程序,那么理论上您可以创建某种插入流,然后执行parseStream.pipe(insertStream)而不是手动处理data事件:D。以下是在另一个文件中创建JSON文件的过滤版本的示例。

fs.createReadStream('myfile.json')
  .pipe(JSONStream.parse('rows.*.doc'))
  .pipe(JSONStream.stringify())
  .pipe(fs.createWriteStream('filtered-myfile.json'));