87

同じストリームからデータを読み取る必要がある 2 つのコマンドを連続して実行する必要があります。ストリームを別のストリームにパイプした後、バッファが空になるため、そのストリームからデータを再度読み取ることができないため、これは機能しません:

var spawn = require('child_process').spawn;
var fs = require('fs');
var request = require('request');

var inputStream = request('http://placehold.it/640x360');
var identify = spawn('identify',['-']);

inputStream.pipe(identify.stdin);

var chunks = [];
identify.stdout.on('data',function(chunk) {
  chunks.push(chunk);
});

identify.stdout.on('end',function() {
  var size = getSize(Buffer.concat(chunks)); //width
  var convert = spawn('convert',['-','-scale',size * 0.5,'png:-']);
  inputStream.pipe(convert.stdin);
  convert.stdout.pipe(fs.createWriteStream('half.png'));
});

function getSize(buffer){
  return parseInt(buffer.toString().split(' ')[2].split('x')[0]);
}

リクエストはこれについて不平を言う

Error: You cannot pipe after data has been emitted from the response.

inputStreamを変更するとfs.createWriteStream、もちろん同じ問題が発生します。私はファイルに書き込みたくありませんが、リクエストが生成するストリーム(またはその他のもの)を何らかの方法で再利用します。

パイプが終了したら、読み取り可能なストリームを再利用する方法はありますか? 上記の例のようなことを達成するための最良の方法は何でしょうか?

4

7 に答える 7

90

2 つのストリームにパイプして、ストリームの複製を作成する必要があります。PassThrough ストリームを使用して単純なストリームを作成できます。これは単に入力を出力に渡すだけです。

const spawn = require('child_process').spawn;
const PassThrough = require('stream').PassThrough;

const a = spawn('echo', ['hi user']);
const b = new PassThrough();
const c = new PassThrough();

a.stdout.pipe(b);
a.stdout.pipe(c);

let count = 0;
b.on('data', function (chunk) {
  count += chunk.length;
});
b.on('end', function () {
  console.log(count);
  c.pipe(process.stdout);
});

出力:

8
hi user
于 2013-10-24T09:13:40.970 に答える
12

最初の答えは、ストリームがデータを処理するのにほぼ同じ時間がかかる場合にのみ機能します。かなり長い時間がかかると、速い方が新しいデータを要求し、その結果、遅い方が使用しているデータを上書きします (複製ストリームを使用して解決しようとした後、この問題が発生しました)。

次のパターンは私にとって非常にうまくいきました。Stream2 ストリーム、Streamz、および Promises に基づくライブラリを使用して、コールバックを介して非同期ストリームを同期します。最初の答えからおなじみの例を使用します。

spawn = require('child_process').spawn;
pass = require('stream').PassThrough;
streamz = require('streamz').PassThrough;
var Promise = require('bluebird');

a = spawn('echo', ['hi user']);
b = new pass;
c = new pass;   

a.stdout.pipe(streamz(combineStreamOperations)); 

function combineStreamOperations(data, next){
  Promise.join(b, c, function(b, c){ //perform n operations on the same data
  next(); //request more
}

count = 0;
b.on('data', function(chunk) { count += chunk.length; });
b.on('end', function() { console.log(count); c.pipe(process.stdout); });
于 2015-11-23T19:38:12.040 に答える
2

一般的な問題の場合、次のコードは正常に機能します

var PassThrough = require('stream').PassThrough
a=PassThrough()
b1=PassThrough()
b2=PassThrough()
a.pipe(b1)
a.pipe(b2)
b1.on('data', function(data) {
  console.log('b1:', data.toString())
})
b2.on('data', function(data) {
  console.log('b2:', data.toString())
})
a.write('text')
于 2015-11-06T09:55:14.390 に答える
1

PassThrough ストリームに非同期操作がある場合、ここに投稿された回答は機能しません。非同期操作で機能するソリューションには、ストリーム コンテンツのバッファリングと、バッファリングされた結果からのストリームの作成が含まれます。

  1. 結果をバッファリングするには、concat-streamを使用できます

    const Promise = require('bluebird');
    const concat = require('concat-stream');
    const getBuffer = function(stream){
        return new Promise(function(resolve, reject){
            var gotBuffer = function(buffer){
                resolve(buffer);
            }
            var concatStream = concat(gotBuffer);
            stream.on('error', reject);
            stream.pipe(concatStream);
        });
    }
    
  2. バッファからストリームを作成するには、次を使用できます。

    const { Readable } = require('stream');
    const getBufferStream = function(buffer){
        const stream = new Readable();
        stream.push(buffer);
        stream.push(null);
        return Promise.resolve(stream);
    }
    
于 2019-05-28T19:18:52.873 に答える
1

2つのストリームに同時に書き込む別のソリューションがあります。当然、書き込み時間は2回の加算になりますが、ダウンロードしたファイルのコピーを保持したいダウンロード要求に応答するためにそれを使用しますサーバー (実際には S3 バックアップを使用しているため、最も使用頻度の高いファイルをローカルにキャッシュして、複数のファイル転送を回避しています)

/**
 * A utility class made to write to a file while answering a file download request
 */
class TwoOutputStreams {
  constructor(streamOne, streamTwo) {
    this.streamOne = streamOne
    this.streamTwo = streamTwo
  }

  setHeader(header, value) {
    if (this.streamOne.setHeader)
      this.streamOne.setHeader(header, value)
    if (this.streamTwo.setHeader)
      this.streamTwo.setHeader(header, value)
  }

  write(chunk) {
    this.streamOne.write(chunk)
    this.streamTwo.write(chunk)
  }

  end() {
    this.streamOne.end()
    this.streamTwo.end()
  }
}

その後、これを通常の OutputStream として使用できます

const twoStreamsOut = new TwoOutputStreams(fileOut, responseStream)

応答または fileOutputStream であるかのようにメソッドに渡します

于 2017-11-08T17:50:37.483 に答える
0

同時にではなく、2 つ以上のストリームにパイプするのはどうですか?

例えば ​​:

var PassThrough = require('stream').PassThrough;
var mybiraryStream = stream.start(); //never ending audio stream
var file1 = fs.createWriteStream('file1.wav',{encoding:'binary'})
var file2 = fs.createWriteStream('file2.wav',{encoding:'binary'})
var mypass = PassThrough
mybinaryStream.pipe(mypass)
mypass.pipe(file1)
setTimeout(function(){
   mypass.pipe(file2);
},2000)

上記のコードはエラーを生成しませんが、file2 は空です

于 2016-11-29T20:14:41.460 に答える