Node.JS中的createReadStream

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

“致命错误:CALL_AND_RETRY_LAST分配失败 – 进程内存不足”

由于fs.readFile()在调用callback之前将整个文件加载到内存中,我应该使用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 } } 

对不起,我对stream式传输很陌生。 下面是正确的方式来做同样的事情,但与stream?

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

谢谢

如果文件是巨大的,那么是的,stream式传输将是你想要处理它。 但是,你在第二个例子中所做的是让stream将所有的文件数据缓冲到内存中,然后处理它。 这和readFile没有什么两样。

你会想看看JSONStream 。 什么stream意味着你想要处理数据stream动。 在你的情况下,你显然必须这样做,因为你不能一次将整个文件缓冲到内存中。 考虑到这一点,希望这样的代码是有道理的:

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

注意它有一种查询模式。 这是因为您不会从文件中获取整个JSON对象/数组,以便一次处理所有JSON对象/数组,所以您必须考虑如何让JSONStream在发现数据时处理数据。

您可以使用JSONStream从本质上查询您感兴趣的JSON数据。这样您就不会将整个文件caching到内存中。 它的缺点是,如果你确实需要所有的数据,那么你将不得不多次stream式传输文件,使用JSONStream在当时只提取你需要的数据,但在你的情况下,你没有很多select。

您也可以使用JSONStream按顺序parsing数据,并将其转储到数据库中。

JSONStream.parse类似于JSON.parse但不返回整个对象,而是返回一个stream。 当parsingstream获取足够的数据以形成与您的查询匹配的整个对象时,它将发出data事件,其中数据是与您的查询匹配的文档。 一旦你configuration了你的数据处理器,你可以将你的读取stream传送到parsingstream中,并观察魔术的发生。

例:

 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); }); 

编辑:

为了进一步了解发生了什么,请尝试像这样思考。 假设你有一个巨大的湖泊,你想把水净化,并把水移到一个新的水库。 如果你有一个巨大的神奇的直升机,有一个巨大的水桶,那么你可以飞过湖面,把湖泊放在水桶里,加上化学处理剂,然后飞到目的地。

当然问题是没有这样的直升机可以处理那么多的重量或体积。 这根本不可能,但这并不意味着我们不能以不同的方式完成我们的目标。 相反,你在湖泊和新水库之间build立一系列河stream(溪stream)。 然后在这些河stream中设置净化站,净化通过它的水。 这些台站可以以各种方式运行。 也许治疗可以做得这么快,以至于你可以让河stream自由stream动,当水以最快的速度沿着河streamstream动时,净化就会发生。

也有可能需要一段时间才能处理水,或者需要一定量的水才能有效地处理水。 所以,你devise河stream时要有闸门,你要控制从湖中stream入河stream的水量,让水站缓冲他们需要的水,直到他们完成工作,然后将净化后的水排放到下游目的地。

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

节点stream很好,因为大部分时间你不必打开和closures门。 当streamcaching一定数量的数据时,节点stream足够智能以控制回stream。 就好像清洗站和湖上的闸门正在互相交谈,以达到完美的stream速。

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

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