Bagaimana cara membuat stream dari string di Node.Js?

177

Saya menggunakan perpustakaan, ya-csv , yang mengharapkan file atau aliran sebagai input, tetapi saya memiliki string.

Bagaimana cara mengubah string itu menjadi aliran di Node?

pathikrit
sumber

Jawaban:

27

Dari node 10.17, stream.Readable memiliki frommetode untuk dengan mudah membuat stream dari setiap iterable (yang mencakup array literal):

const { Readable } = require("stream")

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

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

Perhatikan bahwa setidaknya antara 10,17 dan 12,3, string itu sendiri merupakan iterable, jadi Readable.from("input string")akan berfungsi, tetapi memancarkan satu peristiwa per karakter. Readable.from(["input string"])akan memancarkan satu peristiwa per item dalam array (dalam hal ini, satu item).

Perhatikan juga bahwa pada node-node yang lebih baru (mungkin 12.3, karena dokumentasi mengatakan bahwa fungsi diubah kemudian), tidak perlu lagi membungkus string dalam sebuah array.

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

Fizker
sumber
Menurut stream.Readable.from , Memanggil Readable.from (string) atau Readable.from (buffer) tidak akan memiliki string atau buffer diulang untuk mencocokkan semantik aliran lainnya untuk alasan kinerja.
abbr
Salahku. Fungsi ditambahkan pada 10.7, dan berperilaku seperti yang saya jelaskan sebelumnya. Kadang-kadang sejak itu, string tidak lagi perlu dibungkus dalam array (sejak 12.3, string tidak lagi mengulang setiap karakter secara individual).
Fizker
186

Ketika @substack mengoreksi saya di #node , API aliran baru di Node v10 membuat ini lebih mudah:

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

... setelah itu kamu bisa bebas pipa atau memberikannya kepada konsumen yang Anda tuju.

Ini tidak sebersih resumer one-liner, tetapi tidak menghindari ketergantungan ekstra.

( Pembaruan: di v0.10.26 hingga v9.2.1 sejauh ini, panggilan pushlangsung dari prompt REPL akan macet dengan not implementedpengecualian jika Anda tidak mengatur _read. Itu tidak akan crash di dalam suatu fungsi atau skrip. Jika inkonsistensi membuat Anda gugup, termasuk noop.)

Garth Kidd
sumber
6
Dari dokumen (tautan) : "Semua implementasi aliran yang dapat dibaca harus menyediakan _readmetode untuk mengambil data dari sumber daya yang mendasarinya."
Felix Rabe
2
@eye_mew Anda harus meminta ('stream') terlebih dahulu
Jim Jones
8
Mengapa Anda mendorong nullke buffer aliran?
dopatraman
5
@dopatraman nullmemberitahu stream bahwa ia telah selesai membaca semua data dan untuk menutup stream
chrishiestand
2
Sepertinya Anda tidak harus melakukannya dengan cara ini. Mengutip dokumen : " readable.push()Metode ini dimaksudkan hanya dipanggil oleh Pelaksana yang Dapat Dibaca, dan hanya dari dalam readable._read()metode."
Axel Rauschmayer
127

Jangan gunakan jawaban pelanggan Jo Liss. Ini akan berfungsi dalam kebanyakan kasus, tetapi dalam kasus saya kehilangan saya menemukan bug yang baik 4 atau 5 jam. Tidak perlu modul pihak ketiga untuk melakukan ini.

JAWABAN BARU :

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

Ini harus menjadi aliran yang Dapat Dibaca sepenuhnya sesuai. Lihat di sini untuk info lebih lanjut tentang cara menggunakan stream dengan benar.

JAWABAN TUA : Cukup gunakan aliran PassThrough asli:

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
})

Perhatikan bahwa acara 'tutup' tidak dipancarkan (yang tidak diperlukan oleh antarmuka aliran).

BT
sumber
2
@Finn Anda tidak perlu parens di javascript jika tidak ada argumen
BT
jangan gunakan "var" di 2018! tapi const
stackdave
30

Cukup buat instance baru dari streammodul dan sesuaikan dengan kebutuhan Anda:

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

atau

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');
zirirco
sumber
13
Kode ini memecah konvensi aliran. pipe()seharusnya mengembalikan aliran tujuan, setidaknya.
greim
2
Acara akhir tidak dipanggil jika Anda menggunakan kode ini. Ini bukan cara yang baik untuk membuat aliran yang dapat digunakan secara umum.
BT
12

Sunting: Jawaban Garth mungkin lebih baik.

Teks jawaban lama saya disimpan di bawah ini.


Untuk mengkonversi string ke sungai, Anda dapat menggunakan berhenti melalui aliran:

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

Contoh:

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()
Jo Liss
sumber
Saya tidak bisa mendapatkan solusi zeMirco untuk kasus penggunaan saya, tetapi resumerbekerja dengan cukup baik. Terima kasih!
mpen
Saran pelanggan @substack bekerja sangat baik untuk saya. Terima kasih!
Garth Kidd
2
Resumer hebat, tetapi "auto-resume aliran di nextTick" dapat menghasilkan kejutan jika Anda berharap Anda dapat meneruskan aliran ke konsumen yang tidak dikenal! Saya punya beberapa kode yang menyalurkan aliran konten ke file jika db simpanan metadata berhasil. Itu adalah bug yang mengintai, kebetulan berhasil ketika db write mengembalikan kesuksesan dengan segera! Saya kemudian refactored hal-hal yang berada di dalam blok async, dan boom, alirannya tidak pernah terbaca. Pelajaran: jika Anda tidak tahu siapa yang akan mengkonsumsi aliran Anda, tetap gunakan teknik through (). Pause (). Queue ('string'). End ().
Jolly Roger
1
Saya menghabiskan sekitar 5 jam men-debug kode saya karena saya menggunakan bagian resumer dari jawaban ini. Akan lebih bagus jika Anda bisa .. menghapusnya
BT
10

Ada modul untuk itu: https://www.npmjs.com/package/string-to-stream

var str = require('string-to-stream')
str('hi there').pipe(process.stdout) // => 'hi there' 
Lori
sumber
1
Apakah ini permainan kata "ada aplikasi untuk itu"? ;)
masterxilo
1
Tautan dalam komentar adalah yang bermanfaat: npmjs.com/package/string-to-stream
Dem Pilafian
FYI Saya mencoba menggunakan pustaka ini untuk menulis JSON ke google drive tetapi tidak berhasil untuk saya. Tulis artikel tentang ini di sini: medium.com/@dupski/… . Juga ditambahkan sebagai jawaban di bawah ini
Russell Briggs
6

dalam naskah kopi:

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

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

Gunakan:

new StringStream('text here').pipe(stream1).pipe(stream2)
xinthink
sumber
6

Solusi lain adalah meneruskan fungsi baca ke konstruktor yang Dapat Dibaca (cf doc stream readeable options )

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

Anda bisa setelah menggunakan s.pipe untuk contoh

Philippe T.
sumber
apa tujuan dari pengembalian di akhir?
Kirill Reznikov
"selalu kembalikan sesuatu (atau tidak sama sekali)", ini contoh dari dokumentasi.
Philippe T.
Di JS, jika suatu fungsi tidak memiliki pengembalian, itu setara dengan pengembalian kosong Anda. Bisakah Anda memberikan tautan di mana Anda menemukannya?
Kirill Reznikov
kamu harus benar. Saya mengatakan itu lebih banyak untuk praktik terbaik. Saya tidak ingin mengembalikan apa pun, itu bukan kesalahan. Jadi saya menghapus garis.
Philippe T.
5

Saya bosan harus mempelajarinya kembali setiap enam bulan, jadi saya baru saja menerbitkan modul npm untuk meringkas detail implementasi:

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

Ini adalah inti dari modul:

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;

stradalah stringyang harus diteruskan ke konstruktor setelah invokasi, dan akan dikeluarkan oleh aliran sebagai data. optionsadalah opsi khas yang dapat diteruskan ke aliran, per dokumentasi .

Menurut Travis CI, itu harus kompatibel dengan sebagian besar versi node.

Chris Allen Lane
sumber
2
Ketika saya memposting ini pada awalnya, saya tidak memasukkan kode yang relevan, yang saya diberitahu tidak disukai.
Chris Allen Lane
2

Inilah solusi yang rapi dalam 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...')
Russell Briggs
sumber
1

JavaScript bertipe bebek, jadi jika Anda hanya menyalin API aliran yang dapat dibaca , itu akan bekerja dengan baik. Faktanya, Anda mungkin tidak dapat mengimplementasikan sebagian besar metode tersebut atau membiarkannya saja; yang Anda perlukan untuk mengimplementasikan adalah apa yang digunakan perpustakaan. Anda dapat menggunakan EventEmitterkelas pra-bangun Node untuk menangani acara juga, jadi Anda tidak harus mengimplementasikannyaaddListener dan melakukannya sendiri.

Inilah cara Anda menerapkannya di 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'

Maka Anda bisa menggunakannya seperti ini:

stream = new StringStream someString
doSomethingWith stream
stream.send()
icktoofay
sumber
Saya mendapatkan ini: TypeError: string is not a function at String.CALL_NON_FUNCTION (native) ketika saya menggunakannya sepertinew StringStream(str).send()
pathikrit
Hanya karena JavaScript menggunakan pengetikan bebek bukan berarti Anda harus menemukan kembali kemudi. Node sudah menyediakan implementasi untuk stream. Buat saja instance baru stream.Readableseperti @Garth Kidd yang disarankan.
Sukima
4
@Sukima: stream.Readable tidak ada ketika saya menulis jawaban ini.
icktoofay