繁体   English   中英

暂停Node.js中的可读流

[英]Pausing a readable stream in Node.js

我正在使用csv-to-json (一个简洁的库来处理CSV文件)。

我有一个用例,其中我需要处理大型(> 200万行)CSV并将其插入到数据库中。

为了做到这一点而不会遇到内存问题,我打算将CSV作为流进行处理,每10000行暂停该流,将这些行插入DB中,然后恢复该流。

由于某种原因,我似乎无法pause流。

以下面的代码为例:

const rs = fs.createReadStream("./foo.csv");
rs.pause();

let count = 0;

csv()
.fromStream(rs)
.on("json", (json) => {
  count++;
  console.log(count);
})
.on("done", () => {
  cb(null, count);
})
.on("error", (err) => {
  cb(err);
})

count被记录200次(这是我的CSV中有多少行)-我期望它不记录任何内容,因为流在​​将其传递给fromStream()之前已暂停

这是库的创建者建议的解决方案,在本期中对此进行了跟踪:

var tmpArr=[];
rs.pipe(csv({},{objectMode:true})).pipe(new Writable({
  write: function(json, encoding,callback){
    tmpArr.push(json);
    if (tmpArr.length===10000){
      myDb.save(tmpArr,function(){
        tmpArr=[];
        callback();
      })
    }else{
      callback();
    }
  } ,
  objectMode:true
}))
.on('finish',function(){
  if (tmpArr.length>0){
    myDb.save(tmpArr,function(){
      tmpArr=[];
    })
  }
})

我实际上已经设法像这样通过暂停来模拟暂停,但这并不理想:

let count = 0;
var csvParser=csv()
.fromStream(rs)
.on("json", (json) => {
  rows.push(json);
  if (rows.length % 1000 === 0) {
    rs.unpipe();
    // clear `rows` right after `unpipe`
    const entries = rows;
    rows = [];
    this._insertEntries(db, entries, ()=> {
      rs.pipe(csvParser);
    });
  }
})

我利用了csvtojson也具有fromString(...)方法的事实,并按如下方式使用它。

  1. 使用逐行程序包读取固定数量的行,即10000,并将它们存储在数组中。
  2. 使用lr.pause()暂停逐行阅读器。
  3. 在索引0处插入标头行(如果您的csv文件包含标头行,则使用简单的条件语句忽略逐行阅读器返回的第一行)。
  4. EOL字符连接所有行,这将为您提供该CSV文件的10000行的字符串表示形式。
  5. 使用csvtojson的.fromString(...)将block的字符串表示形式转换为json对象,并将其插入db中。
  6. 通过lr.resume()恢复流,并重复直到逐行阅读器发出'end'事件'end'

这是完整的代码

const CSVToJSON = require("csvtojson");
const LineByLineReader = require("line-by-line");
const { EOL } = require("os");

const BLOCK_LIMIT = 10000;

let lines = [];
let isFirstLineProcessed = false;

const lr = new LineByLineReader("./foo.csv");

lr
.on("line", (line) => {

    // remove this if statement if your CSV does not contain headers line
    if (!isFirstLineProcessed) {
        isFirstLineProcessed = true;
        return;
    }

    lines.push(line);

    if (lines.length === BLOCK_LIMIT) {
        lr.pause();

        // insert headers string ("field1, field2, ...") at index 0;
        lines.splice(0, 0, headers);

        // join all lines using newline operator ("\n") to form a valid csv string
        const csvBlockString = lines.join(EOL);
        const entries = [];

        lines = [];      

        csv()
            .fromString(csvBlockString)
            .on("json", (json) => {
                entries.push(json);
            })
            .on("done", () => {
                this._insertEntries(db, entries, ()=> {
                    lr.resume();
               });
            });
    }
})
.on("end", () => {
    console.log("done");
});

除非修改csv2json库,否则无法执行此操作。

这是您应该首先阅读的链接
https://nodejs.org/dist/latest-v6.x/docs/api/stream.html#stream_three_states

执行rs.pause()时,流处于暂停模式。 即使您不进行操作,实际上也会以暂停模式启动可读流。

该流在3种情况下进入resume

  • 要么有一个.on('data')事件监听器,要么
  • 有一个附加的.pipe()方法或
  • readable.resume()被显式调用。

在您的情况下, fromStream()方法在您的可读流上附加了pipe方法,从而恢复了该流。

参考代码:
https://github.com/Keyang/node-csvtojson/blob/master/libs/core/Converter.js#L378

Converter.prototype.fromStream=function(readStream,cb){
  if (cb && typeof cb ==="function"){
    this.wrapCallback(cb);
  }
  process.nextTick(function(){
    readStream.pipe(this);
  }.bind(this))
  return this;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM