web-dev-qa-db-ja.com

2つ(またはn)のストリームを連結します

  • 2つのストリーム:

    読み取り可能 streamsstream1stream2が与えられた場合、stream1stream2を連結したストリームを取得するの慣用的な(簡潔な)方法は何ですか?

    stream1.pipe(outStream); stream2.pipe(outStream)を実行できません。これは、ストリームの内容が混同されるためです。

  • nストリーム:

    EventEmitter が与えられた場合、これは不確定な数のストリームを放出します。

    eventEmitter.emit('stream', stream1)
    eventEmitter.emit('stream', stream2)
    eventEmitter.emit('stream', stream3)
    ...
    eventEmitter.emit('end')
    

    すべてのストリームを連結したストリームを取得するの慣用的な(簡潔な)方法は何ですか?

19
Jo Liss

combined-stream パッケージはストリームを連結します。 READMEの例:

var CombinedStream = require('combined-stream');
var fs = require('fs');

var combinedStream = CombinedStream.create();
combinedStream.append(fs.createReadStream('file1.txt'));
combinedStream.append(fs.createReadStream('file2.txt'));

combinedStream.pipe(fs.createWriteStream('combined.txt'));

すべてのストリームを一度に追加する必要があると思います。キューが空になると、combinedStreamは自動的に終了します。 問題#5 を参照してください。

stream-stream ライブラリは、明示的な.endを持つ代替ライブラリですが、あまり人気がなく、おそらく十分にテストされていません。 Node 0.10( この説明 を参照)のstreams2APIを使用します。

13
Jo Liss

これはVanillanodejsで実行できます

import { PassThrough } from 'stream'
const merge = (...streams) => {
    let pass = new PassThrough()
    let waiting = streams.length
    for (let stream of streams) {
        pass = stream.pipe(pass, {end: false})
        stream.once('end', () => --waiting === 0 && pass.emit('end'))
    }
    return pass
}
9
Feng

単純な reduce 操作は nodejs で問題ないはずです!

const {PassThrough} = require('stream')

let joined = [s0, s1, s2, ...sN].reduce((pt, s, i, a) => {
  s.pipe(pt, {end: false})
  s.once('end', () => a.every(s => s.ended) && pt.emit('end'))
  return pt
}, new PassThrough())

乾杯;)

9
Ivo

あなたはそれをより簡潔にすることができるかもしれませんが、これがうまくいくものです:

var util = require('util');
var EventEmitter = require('events').EventEmitter;

function ConcatStream(streamStream) {
  EventEmitter.call(this);
  var isStreaming = false,
    streamsEnded = false,
    that = this;

  var streams = [];
  streamStream.on('stream', function(stream){
    stream.pause();
    streams.Push(stream);
    ensureState();
  });

  streamStream.on('end', function() {
    streamsEnded = true;
    ensureState();
  });

  var ensureState = function() {
    if(isStreaming) return;
    if(streams.length == 0) {
      if(streamsEnded)
        that.emit('end');
      return;
    }
    isStreaming = true;
    streams[0].on('data', onData);
    streams[0].on('end', onEnd);
    streams[0].resume();
  };

  var onData = function(data) {
    that.emit('data', data);
  };

  var onEnd = function() {
    isStreaming = false;
    streams[0].removeAllListeners('data');
    streams[0].removeAllListeners('end');
    streams.shift();
    ensureState();
  };
}

util.inherits(ConcatStream, EventEmitter);

状態を追跡するには、streams(ストリームのキュー; Pushを後ろに、shiftを前に)、isStreaming、およびstreamsEnded。新しいストリームを取得したらプッシュし、ストリームが終了するとリスニングを停止してシフトします。ストリームのストリームが終了したら、streamsEndedを設定します。

これらの各イベントで、現在の状態を確認します。すでにストリーミング(ストリームのパイプ)を行っている場合は、何もしません。キューが空でstreamsEndedが設定されている場合、endイベントを発行します。キューに何かがある場合は、再開してそのイベントをリッスンします。

* pauseresumeは助言であるため、一部のストリームは正しく動作しない可能性があり、バッファリングが必要になることに注意してください。この演習は読者に任されています。

これらすべてを行った後、私はn=2ケースは、EventEmitterを作成し、それを使用してConcatStreamを作成し、2つのstreamイベントとそれに続くendイベントを発行します。もっと簡潔にできると思いますが、手元にあるものを使用したほうがよいでしょう。

3
Aaron Dufour

https://github.com/joepie91/node-combined-stream2 は、複合ストリームモジュール(上記で説明)のドロップインStreams2互換の代替品です。Streams1ストリームを自動的にラップします。

Combined-stream2のサンプルコード:

var CombinedStream = require('combined-stream2');
var fs = require('fs');

var combinedStream = CombinedStream.create();
combinedStream.append(fs.createReadStream('file1.txt'));
combinedStream.append(fs.createReadStream('file2.txt'));

combinedStream.pipe(fs.createWriteStream('combined.txt'));
3
pob

streamee.js は、ノード1.0+ストリームに基づくストリームトランスフォーマーとコンポーザーのセットであり、連結メソッドが含まれています。

var stream1ThenStream2 = streamee.concatenate([stream1, stream2]);
1
atamborrino

Vanilla nodejsでは、ECMA 15+を使用し、IvoFengの良い答えを組み合わせています-)。

PassThrough クラスは些細な Transform ストリームであり、ストリームを変更することはありません。

const { PassThrough } = require('stream');

const concatStreams = (streamArray, streamCounter = streamArray.length) => streamArray
  .reduce((mergedStream, stream) => {
    // pipe each stream of the array into the merged stream
    // prevent the automated 'end' event from firing
    mergedStream = stream.pipe(mergedStream, { end: false });
    // rewrite the 'end' event handler
    // Every time one of the stream ends, the counter is decremented.
    // Once the counter reaches 0, the mergedstream can emit its 'end' event.
    stream.once('end', () => --streamCounter === 0 && mergedStream.emit('end'));
    return mergedStream;
  }, new PassThrough());

このように使用できます:

const mergedStreams = concatStreams([stream1, stream2, stream3]);
1
GTPV