如果文件很大,那么是的,流式处理将是您想要的处理方式。但是,您在第二个示例中所做的是将所有文件数据缓冲到内存中,然后在end
。它本质上与readFile
那样。
您会想查看一下JSON流 https://github.com/dominictarr/JSONStream。流式处理意味着您要在数据流过时对其进行处理。在你的情况下,你显然have这样做是因为您无法一次将整个文件缓冲到内存中。考虑到这一点,希望这样的代码有意义:
JSONStream.parse('rows.*.doc')
请注意,它有一种查询模式。这是因为您不会同时使用文件中的整个 JSON 对象/数组,因此您必须更多地考虑希望 JSONStream 如何处理数据当它发现它时.
您可以使用 JSONStream 来查询您感兴趣的 JSON 数据。这样您就永远不会将整个文件缓冲到内存中。它确实有一个缺点,如果您确实需要所有数据,那么您必须多次流式传输文件,使用 JSONStream 只提取当时需要的数据,但在您的情况下,您没有很多选择。
您还可以使用 JSONStream 按顺序解析数据并执行诸如将其转储到数据库之类的操作。
JSONStream.parse
类似于JSON.parse
但它不是返回整个对象,而是返回一个流。当解析流获得足够的数据来形成与您的查询匹配的整个对象时,它将发出data
事件,数据是与您的查询匹配的文档。配置数据处理程序后,您可以将读取流通过管道传输到解析流中,并观察神奇的发生。
Example:
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);
});
Edit:
为了进一步清楚发生了什么,尝试这样思考。假设您有一个巨大的湖泊,您想要对水进行处理以净化它并将水转移到新的水库。如果你有一架带有巨大水桶的巨型魔法直升机,那么你可以飞越湖泊,将湖放入水桶中,向其中添加处理化学品,然后将其飞向目的地。
当然,问题是没有这样的直升机可以处理那么大的重量或体积。这根本不可能,但这并不意味着我们不能以不同的方式实现我们的目标。因此,您在湖泊和新水库之间建造了一系列河流(溪流)。然后,您在这些河流中设立净化站,净化流经河流的水。这些站可以以多种方式运行。也许处理速度可以如此之快,以至于您可以让河流自由流动,当水以最大速度顺流而下时,净化就会发生。
也有可能水需要一些时间才能得到处理,或者该站需要一定量的水才能有效处理水。因此,您将河流设计为有闸门,并控制水从湖泊流入河流的流量,让水站缓冲所需的水,直到它们完成工作并将净化后的水释放到下游并进入最终的水。目的地。
这几乎正是您想要对数据执行的操作。解析流是您的清理站,它会缓冲数据,直到它有足够的数据来形成与您的查询相匹配的整个文档,然后它将该数据推送到下游(并发出data
event).
节点流很好,因为大多数时候您不必处理打开和关闭门的问题。节点流足够智能,可以在流缓冲一定量的数据时控制回流。就好像净化站和湖上的闸门正在相互交谈以计算出完美的流速。
如果您有流数据库驱动程序,那么理论上您可以创建某种插入流,然后执行parseStream.pipe(insertStream)
而不是处理data
手动事件:D。以下是在另一个文件中创建 JSON 文件的筛选版本的示例。
fs.createReadStream('myfile.json')
.pipe(JSONStream.parse('rows.*.doc'))
.pipe(JSONStream.stringify())
.pipe(fs.createWriteStream('filtered-myfile.json'));