Làm cách nào để tạo luồng từ chuỗi trong Node.Js?


177

Tôi đang sử dụng một thư viện, ya-csv , dự kiến ​​một tệp hoặc một luồng làm đầu vào, nhưng tôi có một chuỗi.

Làm cách nào để chuyển đổi chuỗi đó thành một luồng trong Node?

Câu trả lời:


27

Từ nút 10.17, stream.Readable có một fromphương thức để dễ dàng tạo các luồng từ bất kỳ iterable nào (bao gồm cả mảng bằng chữ):

const { Readable } = require("stream")

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

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

Lưu ý rằng ít nhất trong khoảng từ 10.17 đến 12.3, một chuỗi tự nó có thể lặp lại, do đó Readable.from("input string")sẽ hoạt động, nhưng phát ra một sự kiện cho mỗi ký tự. Readable.from(["input string"])sẽ phát ra một sự kiện cho mỗi mục trong mảng (trong trường hợp này là một mục).

Cũng lưu ý rằng trong các nút sau này (có thể là 12.3, vì tài liệu nói rằng hàm đã được thay đổi sau đó), không còn cần thiết phải bọc chuỗi trong một mảng.

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


Theo stream.Readable.from , Calling Readable.from (chuỗi) hoặc Readable.from (buffer) sẽ không có chuỗi hoặc bộ đệm được lặp để khớp với ngữ nghĩa của các luồng khác vì lý do hiệu suất.
abbr

Lỗi của tôi. Hàm được thêm vào 10.7 và hoạt động theo cách tôi mô tả ban đầu. Đôi khi, các chuỗi không còn cần phải được bọc trong các mảng (kể từ 12.3, nó không còn lặp lại từng ký tự riêng lẻ).
Fizker

186

@substack đã sửa tôi trong #node , API luồng mới trong Node v10 giúp việc này dễ dàng hơn:

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

... sau đó bạn có thể tự do ống nó hoặc nếu không vượt qua nó để người tiêu dùng dự định của bạn.

Nó không phải là sạch như resumer một lót, nhưng nó tránh được sự phụ thuộc thêm.

( Cập nhật: từ v0.10.26 đến v9.2.1 cho đến nay, một cuộc gọi đến pushtrực tiếp từ lời nhắc REPL sẽ bị lỗi với một not implementedngoại lệ nếu bạn không đặt _read. Nó sẽ không bị lỗi bên trong một chức năng hoặc tập lệnh. Nếu không nhất quán làm cho bạn lo lắng, bao gồm noop.)


6
Từ các tài liệu (liên kết) : "Tất cả các triển khai luồng có thể đọc phải cung cấp một _readphương thức để tìm nạp dữ liệu từ tài nguyên cơ bản."
Felix Rabe

2
@eye_mew trước tiên bạn cần yêu cầu ('stream')
Jim Jones

8
Tại sao bạn đẩy nullvào bộ đệm của luồng?
dopatraman 23/2/2016

5
@dopatraman nullnói với luồng rằng nó đã đọc xong tất cả dữ liệu và để đóng luồng
chrishiestand

2
Có vẻ như bạn không nên làm theo cách này. Trích dẫn các tài liệu : Quảng cáo readable.push()Phương thức này chỉ được gọi bởi những người triển khai có thể đọc được và chỉ từ bên trong readable._read()phương thức.
Axel Rauschmayer

127

Không sử dụng câu trả lời số liệu của Jo Liss. Nó sẽ hoạt động trong hầu hết các trường hợp, nhưng trong trường hợp của tôi, tôi đã mất 4 hoặc 5 giờ để tìm lỗi. Không cần các mô-đun bên thứ ba để làm điều này.

TRẢ LỜI MỚI :

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

Đây phải là một luồng có thể đọc hoàn toàn tuân thủ. Xem ở đây để biết thêm về cách sử dụng luồng đúng cách.

TRẢ LỜI TRẢ LỜI : Chỉ cần sử dụng luồng PassTh khóa gốc:

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

Lưu ý rằng sự kiện 'đóng' không được phát ra (không bắt buộc bởi các giao diện luồng).


2
@Finn Bạn không cần parens trong javascript nếu không có bất kỳ đối số nào
BT

không 'sử dụng "var" trong năm 2018! nhưng const
stackdave

30

Chỉ cần tạo một phiên bản mới của streammô-đun và tùy chỉnh nó theo nhu cầu của bạn:

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

hoặc là

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
Mã này phá vỡ các quy ước dòng. pipe()được cho là để trả về luồng đích, ít nhất là
chào

2
Sự kiện kết thúc không được gọi nếu bạn sử dụng mã này. Đây không phải là một cách tốt để tạo một luồng có thể được sử dụng nói chung.
BT

12

Chỉnh sửa: Câu trả lời của Garth có lẽ tốt hơn.

Văn bản trả lời cũ của tôi được bảo tồn dưới đây.


Để chuyển đổi một chuỗi thành một luồng, bạn có thể sử dụng một luồng được tạm dừng thông qua :

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

Thí dụ:

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

Tôi không thể làm cho giải pháp của zeMirco hoạt động cho trường hợp sử dụng của mình, nhưng resumerhoạt động khá tốt. Cảm ơn!
mở

Đề xuất số lượng @substack làm việc rất tốt cho tôi. Cảm ơn!
Garth Kidd

2
Resumer là tuyệt vời, nhưng "tự động nối lại luồng trên nextTick" có thể mang lại những bất ngờ nếu bạn mong đợi bạn có thể truyền luồng cho người tiêu dùng chưa biết! Tôi đã có một số mã dẫn luồng nội dung vào một tệp nếu việc lưu siêu dữ liệu db thành công. Đó là một lỗi ẩn, nó đã thành công khi db write trở lại thành công ngay lập tức! Sau này tôi đã cấu trúc lại những thứ nằm trong khối không đồng bộ và bùng nổ, luồng không bao giờ có thể đọc được. Bài học: nếu bạn không biết ai sẽ sử dụng luồng của mình, hãy sử dụng kỹ thuật through (). Pause (). Queue ('chuỗi'). End ().
Jolly Roger

1
Tôi đã dành khoảng 5 giờ để gỡ lỗi mã của mình vì tôi đã sử dụng phần tiếp theo của câu trả lời này. Sẽ thật tuyệt nếu bạn có thể thích .. xóa nó
BT

10

Có một mô-đun cho điều đó: https://www.npmjs.com/package/opes-to-stream

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

1
Đây có phải là một cách chơi chữ "có một ứng dụng cho điều đó" không? ;)
masterxilo

1
Liên kết trong nhận xét là liên kết hữu ích: npmjs.com/package/opes-to-stream
Dem Pilafian

FYI Tôi đã cố gắng sử dụng thư viện này để viết JSON vào google drive nhưng nó không hoạt động với tôi. Đã viết một bài viết về điều này ở đây: Medium.com/@dupski/ . Cũng được thêm vào như một câu trả lời dưới đây
Russell Briggs

6

trong kịch bản cà phê:

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

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

sử dụng nó:

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

6

Một giải pháp khác là chuyển chức năng đọc đến hàm tạo của Readable ( tùy chọn có thể đọc luồng tài liệu cf )

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

bạn có thể sau khi sử dụng s.pipe cho ví dụ


mục đích của sự trở lại vào cuối là gì?
Kirill Reznikov

"Luôn trả lại một cái gì đó (hoặc không có gì)", ví dụ này từ tài liệu.
Philippe T.

Trong JS, nếu một hàm không có trả về thì nó tương đương với trả về trống của bạn. Bạn có thể vui lòng cung cấp một liên kết nơi bạn đã tìm thấy nó?
Kirill Reznikov

bạn nên đúng Tôi nói rằng nhiều hơn cho thực hành tốt nhất. Tôi không muốn trả lại gì cả, đó không phải là một sai lầm. Vì vậy, tôi loại bỏ các dòng.
Philippe T.

5

Tôi đã mệt mỏi vì phải học lại điều này sáu tháng một lần, vì vậy tôi chỉ xuất bản một mô-đun npm để trừu tượng hóa các chi tiết thực hiện:

https://www.npmjs.com/package/streamify- chuỗi

Đây là cốt lõi của mô-đun:

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;

strstringcái phải được chuyển đến hàm tạo khi gọi và sẽ được luồng ra dưới dạng dữ liệu. optionslà các tùy chọn điển hình có thể được truyền cho một luồng, theo tài liệu .

Theo Travis CI, nó phải tương thích với hầu hết các phiên bản của nút.


2
Khi tôi đăng bài này ban đầu, tôi không bao gồm các mã có liên quan, mà tôi đã nói là nhăn mặt.
Chris Allen Lane

2

Đây là một giải pháp gọn gàng trong 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 được gõ bằng vịt, vì vậy nếu bạn chỉ sao chép API của luồng có thể đọc được , nó sẽ hoạt động tốt. Trên thực tế, có lẽ bạn không thể thực hiện hầu hết các phương thức đó hoặc chỉ để chúng dưới dạng sơ khai; tất cả những gì bạn cần thực hiện là những gì thư viện sử dụng. Bạn cũng có thể sử dụng EventEmitterlớp dựng sẵn của Node để xử lý các sự kiện, do đó bạn không phải tự thực hiện addListenervà như vậy.

Đây là cách bạn có thể triển khai nó trong 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'

Sau đó, bạn có thể sử dụng nó như vậy:

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

Tôi nhận được điều này: TypeError: string is not a function at String.CALL_NON_FUNCTION (native) khi tôi sử dụng nó như thếnew StringStream(str).send()
pathikrit

Chỉ vì JavaScript sử dụng kiểu gõ vịt không có nghĩa là bạn nên phát minh lại bánh xe. Nút đã cung cấp một triển khai cho các luồng. Chỉ cần tạo một ví dụ mới stream.Readablenhư @Garth Kidd đề xuất.
Sukima

4
@Sukima: stream.Readable không tồn tại khi tôi viết câu trả lời này.
icktoofay
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.