Node.js 동일한 읽기 가능한 스트림을 여러 (쓰기 가능한) 대상으로 파이핑
동일한 스트림에서 데이터를 읽어야하는 두 개의 명령을 직렬로 실행해야합니다. 스트림을 다른 스트림으로 파이프 한 후 버퍼가 비워져 해당 스트림에서 데이터를 다시 읽을 수 없으므로 작동하지 않습니다.
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
동일한 문제가 발생합니다. 나는 파일에 쓰고 싶지 않지만 요청이 생성 하는 스트림 (또는 그 문제에 대해 다른 것) 을 어떤 방식 으로든 재사용 하고 싶습니다 .
배관이 끝나면 읽을 수있는 스트림을 재사용 할 수있는 방법이 있습니까? 위의 예와 같은 작업을 수행하는 가장 좋은 방법은 무엇입니까?
두 개의 하천으로 배관하여 하천의 복제본을 만들어야합니다. 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); });
동시에 두 개 이상의 하천으로 배관하는 것은 어떻습니까?
예 :
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는 비어 있습니다.
For general problem, the following code works fine
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')
If you have async operations on the PassThrough streams, the answers posted here won't work. A solution that works for async operations includes buffering the stream content and then creating streams from the buffered result.
To buffer the result you can use 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); }); }
To create streams from the buffer you can use:
const { Readable } = require('stream'); const getBufferStream = function(buffer){ const stream = new Readable(); stream.push(buffer); stream.push(null); return Promise.resolve(stream); }
I have a different solution to write to two streams simultaneously, naturally, the time to write will be the addition of the two times, but I use it to respond to a download request, where I want to keep a copy of the downloaded file on my server (actually I use a S3 backup, so I cache the most used files locally to avoid multiple file transfers)
/**
* 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()
}
}
You can then use this as a regular OutputStream
const twoStreamsOut = new TwoOutputStreams(fileOut, responseStream)
and pass it to to your method as if it was a response or a fileOutputStream
'developer tip' 카테고리의 다른 글
사용하지 않는 가져 오기 및 개체가 성능에 영향을 미칩니 까? (0) | 2020.11.21 |
---|---|
FileWriter와 BufferedWriter의 Java 차이점 (0) | 2020.11.21 |
단일 2D 배열을 할당하는 것이 전체 크기와 모양이 동일한 여러 1D 배열을 할당하는 루프보다 더 오래 걸리는 이유는 무엇입니까? (0) | 2020.11.21 |
모든 EntityManager를 close ()해야합니까? (0) | 2020.11.21 |
CSS, 중앙 div, 크기에 맞게 축소? (0) | 2020.11.21 |