同じストリームからデータを読み取る必要がある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
に変更すると、もちろん同じ問題が発生します。私はファイルに書き込みたくないが、reuse何らかの方法でrequestが生成するストリーム(またはその問題)。
パイピングが終了したら、読み取り可能なストリームを再利用する方法はありますか?上記の例のようなことを達成する最良の方法は何でしょうか?
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
最初の答えは、ストリームがデータの処理にほぼ同じ時間を要する場合にのみ機能します。かなり長い時間がかかると、速い方が新しいデータを要求し、その結果、遅い方がまだ使用しているデータを上書きします(重複したストリームを使用して解決しようとした後、この問題が発生しました)。
次のパターンは非常にうまく機能しました。 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); });
同時にではなく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は空です
一般的な問題については、次のコードは正常に動作します
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')
PassThroughストリームで非同期操作がある場合、ここに投稿された回答は機能しません。非同期操作で機能するソリューションには、ストリームコンテンツのバッファリングと、バッファリングされた結果からのストリームの作成が含まれます。
結果をバッファするには、 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);
});
}
バッファからストリームを作成するには、次を使用できます。
const { Readable } = require('stream');
const getBufferStream = function(buffer){
const stream = new Readable();
stream.Push(buffer);
stream.Push(null);
return Promise.resolve(stream);
}
私は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のようにメソッドに渡します