如何从Node.Js中的字符串创建流?


Answers:


25

从节点10.17开始,stream.Readable有一个from方法可以轻松地从任何可迭代对象(包括数组文字)创建流:

const { Readable } = require("stream")

const readable = Readable.from(["input string"])

readable.on("data", (chunk) => {
  console.log(chunk) // will be called once with `"input string"`
})

请注意,至少在10.17和12.3之间,字符串本身是可迭代的,因此Readable.from("input string")可以工作,但每个字符发出一个事件。Readable.from(["input string"])将为数组中的每个项目发出一个事件(在这种情况下为一个项目)。

还要注意,在以后的节点中(可能是12.3,因为文档说函数在那时被更改了),所以不再需要将字符串包装在数组中。

https://nodejs.org/api/stream.html#stream_stream_可读_from_iterable_options


根据stream.Readable.from的说法,出于性能原因
简称

我的错。该函数是在10.7中添加的,其行为与我最初描述的方式相同。从那时起,不再需要将字符串包装在数组中了(从12.3开始,它不再单独迭代每个字符)。
Fizker

185

作为@substack纠正我#node,新的流API在节点V10使这更容易:

const Readable = require('stream').Readable;
const s = new Readable();
s._read = () => {}; // redundant? see update below
s.push('your text here');
s.push(null);

...之后就可以自由地或以其他方式把它传递给你的目标消费者。

它不像恢复器一线那么干净,但是确实避免了额外的依赖。

更新:到目前为止,在v0.10.26到v9.2.1中,如果您未设置,push直接从REPL提示符处直接调用将会崩溃,并且不会发生not implemented异常_read。它不会在函数或脚本内崩溃。紧张,包括noop。)


6
来自文档(链接):“所有可读流实现都必须提供一种_read从底层资源获取数据的方法。”
Felix Rabe 2014年

2
@eye_mew,您需要先要求(“ stream”)
Jim Jones,

8
为什么要推null入流的缓冲区?
dopatraman '16

5
@dopatraman null告诉流它已经完成了所有数据的读取并关闭了流
chrishiestand

2
看来您不应该这样做。引用文档:“该readable.push()方法仅应由可读实现者调用,并且只能从该readable._read()方法内部调用。”
Axel Rauschmayer

127

不要使用Jo Liss的履历表答案。它在大多数情况下都可以使用,但就我而言,它使我损失了4到5个小时的良好错误查找时间。不需要第三方模块来执行此操作。

新答案

var Readable = require('stream').Readable

var s = new Readable()
s.push('beep')    // the string you want
s.push(null)      // indicates end-of-file basically - the end of the stream

这应该是完全兼容的Readable流。有关如何正确使用流的更多信息,请参见此处

老答案:只需使用本地PassThrough流:

var stream = require("stream")
var a = new stream.PassThrough()
a.write("your string")
a.end()

a.pipe(process.stdout) // piping will work as normal
/*stream.on('data', function(x) {
   // using the 'data' event works too
   console.log('data '+x)
})*/
/*setTimeout(function() {
   // you can even pipe after the scheduler has had time to do other things
   a.pipe(process.stdout) 
},100)*/

a.on('end', function() {
    console.log('ended') // the end event will be called properly
})

请注意,不会发出“关闭”事件(流接口不需要)。


2
@Finn如果没有任何参数,则不需要使用javascript中的括号
-BT

不要在2018年使用“ var”!但const
stackdave

30

只需创建stream模块的新实例并根据需要自定义它:

var Stream = require('stream');
var stream = new Stream();

stream.pipe = function(dest) {
  dest.write('your string');
  return dest;
};

stream.pipe(process.stdout); // in this case the terminal, change to ya-csv

要么

var Stream = require('stream');
var stream = new Stream();

stream.on('data', function(data) {
  process.stdout.write(data); // change process.stdout to ya-csv
});

stream.emit('data', 'this is my string');

13
此代码违反了流约定。pipe()至少应该返回目标流。
greim

2
如果使用此代码,则不会调用end事件。这不是创建可普遍使用的流的好方法。
BT

12

编辑: 加思的答案可能更好。

我的旧答案文本保留在下面。


将一个字符串转换成流,你可以使用一个暂停通过流:

through().pause().queue('your string').end()

例:

var through = require('through')

// Create a paused stream and buffer some data into it:
var stream = through().pause().queue('your string').end()

// Pass stream around:
callback(null, stream)

// Now that a consumer has attached, remember to resume the stream:
stream.resume()

对于我的用例,我无法获得zeMirco的解决方案,但resumer效果很好。谢谢!
mpen 2013年

@substack简历建议对我来说非常有效。谢谢!
加思·基德

2
Resumer很棒,但是如果您希望可以将流传递给未知的使用者,则“在nextTick上自动恢复流”可能会带来惊喜!如果成功保存元数据,我有一些代码将内容流通过管道传输到文件。那是一个潜伏的错误,当数据库写入立即返回成功时,它碰巧成功了!后来我将事物重构为异步块内,并且繁荣起来,该流从不可读。课程:如果您不知道谁将消耗您的流,请坚持使用through()。pause()。queue('string')。end()技术。
乔莉·罗杰

1
我花了大约5个小时来调试我的代码,因为我使用了此答案的恢复部分。如果您愿意,那就太好了..删除它
BT

10

有一个用于此的模块:https : //www.npmjs.com/package/string-to-stream

var str = require('string-to-stream')
str('hi there').pipe(process.stdout) // => 'hi there' 

1
这是“有一个应用程序”的双关语吗?;)
masterxilo

1
评论中的链接是有用的链接:npmjs.com/package/string-to-stream
Dem Pilafian

仅供参考,我尝试使用此库将JSON写入Google驱动器,但对我而言不起作用。在此处撰写有关此内容的文章:medium.com/@dupski/…。还在下面添加为答案
罗素·布里格斯

6

在咖啡脚本中:

class StringStream extends Readable
  constructor: (@str) ->
    super()

  _read: (size) ->
    @push @str
    @push null

用它:

new StringStream('text here').pipe(stream1).pipe(stream2)

6

另一种解决方案是将read函数传递给Readable的构造函数(参见doc 流readeable选项

var s = new Readable({read(size) {
    this.push("your string here")
    this.push(null)
  }});

您可以在使用s.pipe之后作为示例


最后退货的目的是什么?
Kirill Reznikov

从文档中可以看出,“总是返回某些东西(或什么都不返回)”。
Philippe T.

在JS中,如果一个函数没有返回值,则它等于空返回值。您能否在找到它的地方提供链接?
Kirill Reznikov

你应该对。我说的更多是最佳实践。我什么也不想退,这不是错误。所以我删除线。
Philippe T.

5

我厌倦了必须每六个月重新学习一次,所以我刚刚发布了一个npm模块来抽象实现细节:

https://www.npmjs.com/package/streamify-string

这是模块的核心:

const Readable = require('stream').Readable;
const util     = require('util');

function Streamify(str, options) {

  if (! (this instanceof Streamify)) {
    return new Streamify(str, options);
  }

  Readable.call(this, options);
  this.str = str;
}

util.inherits(Streamify, Readable);

Streamify.prototype._read = function (size) {

  var chunk = this.str.slice(0, size);

  if (chunk) {
    this.str = this.str.slice(size);
    this.push(chunk);
  }

  else {
    this.push(null);
  }

};

module.exports = Streamify;

strstring在调用时必须传递给构造函数的,并将由流作为数据输出。根据文档options,是可以传递到流的典型选项。

根据Travis CI,它应该与大多数版本的节点兼容。


2
当我最初发布此代码时,我没有包括相关代码,但有人告诉我我对此不满意。
克里斯·艾伦·莱恩

2

这是TypeScript中的整洁解决方案:

import { Readable } from 'stream'

class ReadableString extends Readable {
    private sent = false

    constructor(
        private str: string
    ) {
        super();
    }

    _read() {
        if (!this.sent) {
            this.push(Buffer.from(this.str));
            this.sent = true
        }
        else {
            this.push(null)
        }
    }
}

const stringStream = new ReadableString('string to be streamed...')

1

JavaScript是鸭式的,因此,如果您仅复制可读流的API,它将可以正常工作。实际上,您可能无法实现其中的大多数方法,或者仅将其保留为存根。您需要实现的只是库使用的内容。您也可以使用Node的预构建EventEmitter来处理事件,因此您不必addListener自己实现。

这是在CoffeeScript中实现它的方式:

class StringStream extends require('events').EventEmitter
  constructor: (@string) -> super()

  readable: true
  writable: false

  setEncoding: -> throw 'not implemented'
  pause: ->    # nothing to do
  resume: ->   # nothing to do
  destroy: ->  # nothing to do
  pipe: -> throw 'not implemented'

  send: ->
    @emit 'data', @string
    @emit 'end'

然后,您可以像这样使用它:

stream = new StringStream someString
doSomethingWith stream
stream.send()

我明白了: TypeError: string is not a function at String.CALL_NON_FUNCTION (native) 当我使用它时new StringStream(str).send()
pathikrit 2012年

仅仅因为JavaScript使用鸭子类型并不意味着您应该重新发明轮子。节点已经提供了流的实现。只需创建一个stream.Readable像@Garth Kidd建议的新实例。
Sukima 2014年

4
@Sukima:我写这个答案时stream.Readable 不存在
2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.