Node.js 连接两个(或n个)流
2条溪流: 给定可读的Node.js 连接两个(或n个)流,node.js,stream,eventemitter,Node.js,Stream,Eventemitter,2条溪流: 给定可读的stream1和stream2,获取包含stream1和stream2连接的流的惯用(简洁)方法是什么 我不能做stream1.管道(外扩);stream2.pipe(outStream),因为这样流内容就混在一起了 n个流: 给定一个发射不确定数量流的 eventEmitter.emit('stream', stream1) eventEmitter.emit('stream', stream2) eventEmitter.emit('stream', stream3)
stream1
和stream2
,获取包含stream1
和stream2
连接的流的惯用(简洁)方法是什么
我不能做stream1.管道(外扩);stream2.pipe(outStream)
,因为这样流内容就混在一起了
eventEmitter.emit('stream', stream1)
eventEmitter.emit('stream', stream2)
eventEmitter.emit('stream', stream3)
...
eventEmitter.emit('end')
用什么惯用的(简洁的)方法获得所有流连接在一起的流你也许可以让它更简洁,但这里有一个可行的方法:
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);
我们通过流
(流队列;推
到后面,从前面移动,正在流
,以及流发送
)跟踪状态。当我们得到一个新的流时,我们推它,当一个流结束时,我们停止倾听并移动它。当流结束时,我们设置streamsend
在每一个事件中,我们都会检查我们所处的状态。如果我们已经在流(管道流),我们什么也不做。如果队列为空并且设置了streamsend
,我们将发出end
事件。如果队列中有内容,我们将恢复它并侦听其事件
*请注意,pause
和resume
是建议性的,因此某些流可能无法正常运行,需要缓冲。这个练习留给读者做
在完成所有这些之后,我将通过构造一个EventEmitter
,用它创建一个ConcatStream
,然后发出两个stream
事件,然后是一个end
事件来执行n=2
案例。我相信这可以做得更简洁一些,但我们不妨利用我们现有的资源。这个包将流连接起来。自述文件中的示例:
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'));
我相信您必须一次附加所有流。如果队列为空,则组合流将自动结束。看
该库是一个具有显式.end
的替代方案,但它的受欢迎程度要低得多,而且可能没有经过很好的测试。它使用节点0.10的streams2 API(请参阅)。是一组基于节点1.0+流的流转换器和生成器,包括一种连接方法:
var stream1ThenStream2 = streamee.concatenate([stream1, stream2]);
是与Streams2兼容的组合流模块(如上所述)的替代品。它自动包装Streams1流
组合流2的示例代码:
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'));
这可以用香草nodej来完成
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
}
如果您不关心流中数据的顺序,那么在中只需一个简单的操作就可以了
干杯;) 使用ECMA 15+并结合Ivo和Feng的良好答案,对香草NODEJ进行 该类是一个普通流,它不会以任何方式修改流
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]);
下面的代码适用于我:)。从前面给出的所有答案中获取输入
const pipeStreams = (streams) => {
const out = new PassThrough()
// Piping the first stream to the out stream
// Also prevent the automated 'end' event of out stream from firing
streams[0].pipe(out, { end: false })
for (let i = 0; i < streams.length - 2; i++) {
// On the end of each stream (until the second last) pipe the next stream to the out stream
// Prevent the automated 'end' event of out stream from firing
streams[i].on('end', () => {
streams[i + 1].pipe(out, { end: false })
})
}
// On the end of second last stream pipe the last stream to the out stream.
// Don't prevent the 'end flag from firing'
streams[streams.length - 2].on('end', () => {
streams[streams.length - 1].pipe(out)
})
return out
}
constpipestreams=(streams)=>{
const out=new PassThrough()
//将第一条流输送到输出流
//还可以防止触发out stream的自动“结束”事件
流[0]。管道(输出,{end:false})
for(设i=0;i{
流[i+1]。管道(输出,{end:false})
})
}
//在第二个最后一个流的末端,将最后一个流输送到输出流。
//不要阻止“结束标志发射”
streams[streams.length-2]。在('end',()=>{
streams[streams.length-1]。管道(输出)
})
返回
}
这里两个投票最多的答案都不适用于异步流,因为不管源流是否已准备好生成,它们只是通过管道传输。我必须将内存中的字符串流与来自数据库的数据源相结合,并且数据库内容始终位于结果流的末尾,因为获得db响应需要一秒钟的时间。以下是我为自己的目的而写的东西
export function joinedStream(...streams: Readable[]): Readable {
function pipeNext(): void {
const nextStream = streams.shift();
if (nextStream) {
nextStream.pipe(out, { end: false });
nextStream.on('end', function() {
pipeNext();
});
} else {
out.end();
}
}
const out = new PassThrough();
pipeNext();
return out;
}
现在可以使用异步迭代器轻松地完成此操作
异步函数*concatStreams(可读){
for(可读内容的常量){
对于wait(const chunk of readable){yield chunk}
}
}
你可以这样使用它
const fs=require('fs'))
const stream=require('stream')
const files=['file1.txt','file2.txt','file3.txt']
const iterable=wait concatStreams(files.map(f=>fs.createReadStream(f)))
//将异步iterable转换为可读流
const mergedStream=stream.Readable.from(iterable)
有关异步迭代器的更多信息:谢谢Aaron!我有点希望会有一些现有的库,这样我可以用三行代码来解决它。如果没有,我想我可能会将您的解决方案提取到一个包中。我可以在麻省理工许可证下使用你的代码吗?啊,找到了流库。看看我的答案。@JoLiss我也先找了些东西,但没找到那个选项。如果你还想的话,你当然可以在库中使用我的代码。谢谢,我会查出来的。我想这就是节点0.10?是的节点0.10,但您可以将旧式流包装为0.10+流,如READMEShouldn中所述。您不是从reduce返回了一些内容吗?这看起来像是未定义的
joined
。警告:这将导致所有流并行地通过管道传输到直通流,而不考虑数据的顺序,很可能会损坏您的数据。@LeonLi这确实是此方法的目的。如果要保留顺序,可以将不同于PassThrough的初始值传递给reduce函数;)@Ivo这个问题问的是连接。因此,本QA中的大多数读者都关心订购。
export function joinedStream(...streams: Readable[]): Readable {
function pipeNext(): void {
const nextStream = streams.shift();
if (nextStream) {
nextStream.pipe(out, { end: false });
nextStream.on('end', function() {
pipeNext();
});
} else {
out.end();
}
}
const out = new PassThrough();
pipeNext();
return out;
}