实现缓冲的转换流

时间:2013-12-01 21:57:02

标签: javascript node.js stream

我正在尝试使用new Node.js streams API来实现一个缓冲一定数量数据的流。当此流通过管道传输到另一个流时,或者某些内容消耗readable个事件时,此流应刷新其缓冲区,然后简单地成为传递。问题是,此流将通过管道传输到许多其他流,并且当连接每个目标流时,即使已将其刷新到另一个流,也必须刷新缓冲区

例如:

  1. BufferStream实现stream.Transform,并保留512KB内部环缓冲区
  2. ReadableStreamA通过管道传输到BufferStream
  3. 的实例
  4. BufferStream写入其环形缓冲区,从ReadableStreamA读取数据。(数据丢失无关紧要,因为缓冲区会覆盖旧数据。)
  5. BufferStream通过管道传输到WritableStreamB
  6. WritableStreamB接收整个512KB缓冲区,并在从ReadableStreamABufferStream写入数据时继续获取数据。
  7. BufferStream通过管道传输到WritableStreamC
  8. WritableStreamC也会收到整个512KB缓冲区,但此缓冲区现在与WritableStreamB收到的缓冲区不同,因为此后已将更多数据写入BufferStream
  9. 流API是否可以实现?我能想到的唯一方法是创建一个带有方法的对象,该方法为每个目标创建一个新的PassThrough流,这意味着我不能简单地管道输入和输出。

    对于它的价值,我通过简单地在data事件上监听新的处理程序,使用旧的“流动”API完成了这项工作。当一个新函数附加.on('data')时,我会直接使用环形缓冲区的副本来调用它。

2 个答案:

答案 0 :(得分:7)

这是我对你的问题的看法。

基本思想是创建一个Transform流,这将允许我们在流输出上发送数据之前执行自定义缓冲逻辑:

var util = require('util')
var stream = require('stream')

var BufferStream = function (streamOptions) {
  stream.Transform.call(this, streamOptions)
  this.buffer = new Buffer('')
}

util.inherits(BufferStream, stream.Transform)

BufferStream.prototype._transform = function (chunk, encoding, done) {
  // custom buffering logic
  // ie. add chunk to this.buffer, check buffer size, etc.
  this.buffer = new Buffer(chunk)

  this.push(chunk)
  done()
}

然后,我们需要覆盖.pipe()方法,以便在BufferStream通过管道传输到流中时通知我们,这样我们就可以自动将数据写入其中:

BufferStream.prototype.pipe = function (destination, options) {
  var res = BufferStream.super_.prototype.pipe.call(this, destination, options)
  res.write(this.buffer)
  return res
}

这样,当我们编写buffer.pipe(someStream)时,我们按预期执行管道并将内部缓冲区写入输出流。在那之后,Transform课程会处理所有事情,同时跟踪背压等等。

这是working gist。请注意,我没有打扰写一个正确的缓冲逻辑(即我不关心内部缓冲区的大小),但这应该很容易修复。

答案 1 :(得分:1)

保罗的回答很好,但我不认为它符合确切的要求。听起来需要发生的是每次在此转换流上调用pipe()时,它需要首先刷新缓冲区,该缓冲区表示在创建转换流/(连接到源流)之间的所有数据累积和它连接到当前可写/目标流的时间。

这样的事情可能更正确:

s = """gene   cell_1      cell_2  
MYC    5.0     P   4.0     A
AKT    3.0     A   1.0     P"""

import pandas as pd
from io import StringIO
in_file = StringIO(s)

pd.read_fwf(in_file)
Out[6]: 
  gene  cell_1 Unnamed: 2  cell_2 Unnamed: 4
0  MYC     5.0          P     4.0          A
1  AKT     3.0          A     1.0          P

我想这个:

  var BufferStream = function () {
        stream.Transform.apply(this, arguments);
        this.buffer = []; //I guess an array will do
    };

    util.inherits(BufferStream, stream.Transform);

    BufferStream.prototype._transform = function (chunk, encoding, done) {

        this.push(chunk ? String(chunk) : null);
        this.buffer.push(chunk ? String(chunk) : null);

        done()
    };

    BufferStream.prototype.pipe = function (destination, options) {
        var res = BufferStream.super_.prototype.pipe.apply(this, arguments);
        this.buffer.forEach(function (b) {
            res.write(String(b));
        });
        return res;
    };


    return new BufferStream();

相当于:

BufferStream.super_.prototype.pipe.apply(this, arguments);

你可以优化它并在调用管道/管道时使用一些标志。