วิธีการสร้างสตรีมจากสตริงใน Node.Js?


177

ฉันใช้ไลบรารีya-csvซึ่งคาดว่าไฟล์หรือสตรีมเป็นอินพุต แต่ฉันมีสตริง

ฉันจะแปลงสตริงนั้นเป็นสตรีมในโหนดได้อย่างไร

คำตอบ:


27

จากโหนด 10.17 fromสตรีมอ่านได้มีวิธีสร้างสตรีมจาก iterable ใด ๆ ได้ง่าย (ซึ่งรวมถึงตัวอักษรอาร์เรย์):

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 สตริงเป็นตัว iterable ดังนั้นReadable.from("input string")จะใช้งานได้ แต่ปล่อยหนึ่งเหตุการณ์ต่อตัวละคร Readable.from(["input string"])จะปล่อยหนึ่งเหตุการณ์ต่อรายการในอาร์เรย์ (ในกรณีนี้หนึ่งรายการ)

นอกจากนี้โปรดทราบว่าในโหนดภายหลัง (อาจเป็น 12.3 เนื่องจากเอกสารระบุว่าฟังก์ชั่นเปลี่ยนไปแล้ว) จึงไม่จำเป็นต้องห่อสตริงในอาร์เรย์อีกต่อไป

https://nodejs.org/api/stream.html#stream_stream_readable_from_iterable_options


ตามstream.Readable.from , โทร Readable.from (สตริง) หรือ Readable.from (กันชน) จะไม่ได้มีสายหรือบัฟเฟอร์จะซ้ำเพื่อให้ตรงกับความหมายอื่น ๆ ลำธารเพื่อเหตุผลด้านประสิทธิภาพ
abbr

ความผิดฉันเอง. ฟังก์ชั่นถูกเพิ่มเข้ามาใน 10.7 และทำตัวตามที่ฉันอธิบาย บางครั้งเนื่องจากไม่จำเป็นต้องห่อสตริงในอาร์เรย์อีกต่อไป (ตั้งแต่ 12.3 มันจะไม่ซ้ำแต่ละอักขระอีกต่อไป)
Fizker

186

เป็น@substackแก้ไขฉันใน#node , สตรีม APIใหม่ใน Node v10 ทำให้ง่ายขึ้น:

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

... หลังจากที่คุณได้อย่างอิสระสามารถท่อหรือมิฉะนั้นจะผ่านมันไปยังผู้บริโภคที่คุณตั้งใจ

มันไม่สะอาดเท่าresumer one-liner แต่จะหลีกเลี่ยงการพึ่งพาพิเศษ

( อัปเดต:ใน v0.10.26 ถึง v9.2.1 จนถึงตอนนี้การเรียกไปยังpushพรอมต์ REPL โดยตรงจะล้มเหลวโดยมีnot implementedข้อยกเว้นหากคุณไม่ได้ตั้งค่า_readมันจะไม่ผิดพลาดภายในฟังก์ชันหรือสคริปต์หากความไม่สอดคล้องทำให้คุณ ประสาทรวมถึงnoop.)


6
จากเอกสาร (ลิงก์) : "การใช้งานสตรีมที่สามารถอ่านได้ทั้งหมดจะต้องมี_readวิธีการดึงข้อมูลจากแหล่งข้อมูลพื้นฐาน"
เฟลิกซ์รา

2
@eye_mew คุณจำเป็นต้องมี ('สตรีม') ก่อน
Jim Jones

8
ทำไมคุณดันnullเข้าไปในบัฟเฟอร์ของกระแส
dopatraman

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

นี่ควรเป็นสตรีมแบบอ่านได้ที่เข้ากันได้อย่างสมบูรณ์ ดูที่นี่สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีใช้สตรีมอย่างเหมาะสม

คำตอบเก่า : เพียงใช้สตรีม 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 คุณไม่จำเป็นต้องมี parens ใน javascript หากไม่มี args ใด ๆ
BT

อย่าใช้ "var" ในปี 2561! แต่ 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
เหตุการณ์สุดท้ายจะไม่ถูกเรียกถ้าคุณใช้รหัสนี้ นี่ไม่ใช่วิธีที่ดีในการสร้างสตรีมที่สามารถใช้งานได้ทั่วไป
BT

12

แก้ไข: คำตอบของ Garthน่าจะดีกว่า

ข้อความคำตอบเดิมของฉันถูกเก็บไว้ด้านล่าง


ในการแปลงสตริงเป็นสตรีมคุณสามารถใช้หยุดชั่วคราวผ่านสตรีม:

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

คำแนะนำการ resumer ของ @substack ทำงานได้ดีมากสำหรับฉัน ขอบคุณ!
การ์ ธ Kidd

2
การนับใหม่นั้นยอดเยี่ยม แต่ "การดำเนินการสตรีมต่อโดยอัตโนมัติในรายการถัดไป" จะทำให้คุณประหลาดใจหากคุณคาดว่าคุณจะสามารถส่งกระแสข้อมูลไปยังผู้บริโภคที่ไม่รู้จัก! ฉันมีรหัสบางอย่างที่ส่งกระแสข้อมูลเนื้อหาไปยังไฟล์ถ้าบันทึก db ของข้อมูลเมตาสำเร็จ นั่นเป็นข้อบกพร่องที่ซุ่มซ่อนมันเกิดขึ้นเพื่อประสบความสำเร็จเมื่อ db เขียนกลับมาประสบความสำเร็จทันที! ต่อมาฉันปรับปรุงสิ่งต่าง ๆ ให้อยู่ในบล็อกแบบอะซิงก์และบูมสตรีมก็ไม่สามารถอ่านได้ บทเรียน: หากคุณไม่รู้ว่าใครกำลังบริโภคสตรีมของคุณให้ทำตามเทคนิค (). หยุดชั่วคราว (). คิว ('สตริง'). end ()
Jolly Roger

1
ฉันใช้เวลาประมาณ 5 ชั่วโมงในการดีบักรหัสของฉันเพราะฉันใช้หมายเลข resumer ของคำตอบนี้ มันจะดีถ้าคุณต้องการ .. ลบออก
BT

10

มีโมดูลสำหรับที่: https://www.npmjs.com/package/string-to- สตรีม

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

FYI ฉันพยายามใช้ไลบรารีนี้เพื่อเขียน JSON ไปยัง google ไดรฟ์ แต่ใช้ไม่ได้สำหรับฉัน เขียนบทความเกี่ยวกับสิ่งนี้ได้ที่นี่: medium.com/@dupski/… . เพิ่มเป็นคำตอบด้านล่างด้วยเช่นกัน
Russell Briggs

6

ในกาแฟสคริปต์:

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

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

ใช้มัน:

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

6

โซลูชันอื่นกำลังส่งผ่านฟังก์ชันการอ่านไปยังตัวสร้างของ Readable ( ตัวเลือก readeable cf doc stream )

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

คุณสามารถใช้ s.pipe ได้หลังจากเป็นตัวอย่าง


จุดประสงค์ของการกลับมาในตอนท้ายคืออะไร?
Kirill Reznikov

"ส่งคืนบางสิ่ง (หรือไม่มีอะไร)" เสมอตัวอย่างนี้จากเอกสารประกอบ
Philippe T.

ใน JS หากฟังก์ชั่นไม่มีการคืนค่ามันก็เท่ากับผลตอบแทนที่คุณว่างเปล่า คุณช่วยระบุลิงค์ที่คุณเจอได้ไหม?
คิริลล์ 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;

strเป็นสิ่งstringที่ต้องส่งผ่านไปยัง Constructor เมื่อทำการเรียกใช้และจะถูกส่งออกโดยกระแสข้อมูลในรูปของข้อมูล optionsเป็นตัวเลือกที่ทั่วไปที่อาจจะถูกส่งผ่านไปยังกระแสต่อเอกสาร

ตาม Travis CI มันควรจะเข้ากันได้กับโหนดส่วนใหญ่รุ่น


2
เมื่อฉันโพสต์สิ่งนี้ในตอนแรกฉันไม่ได้รวมรหัสที่เกี่ยวข้องซึ่งฉันบอกว่าถูกขมวดคิ้ว
Chris Allen Lane

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 ของสตรีมที่อ่านได้มันจะทำงานได้ดี ในความเป็นจริงคุณอาจไม่สามารถใช้วิธีการเหล่านั้นส่วนใหญ่หรือเพียงแค่ปล่อยให้พวกเขาเป็นไม่สมบูรณ์; สิ่งที่คุณต้องใช้คือสิ่งที่ห้องสมุดใช้ คุณสามารถใช้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

เพียงเพราะจาวาสคริปต์ใช้การพิมพ์เป็ดไม่ได้หมายความว่าคุณควรบูรณาการล้อ โหนดจัดเตรียมการนำไปใช้งานสำหรับสตรีมแล้ว เพียงแค่สร้างอินสแตนซ์ใหม่ของstream.Readablelike @Garth Kidd
Sukima

4
@Sukima: stream.Readable ไม่มีตัวตนเมื่อฉันเขียนคำตอบนี้
icktoofay
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.