Как мне прочитать содержимое потока Node.js в строковую переменную?

Я взломал программу Node, которая использует smtp-protocol для захвата писем SMTP и обработки почтовых данных. Библиотека предоставляет данные почты в виде потока, и я не знаю, как передать их в строку.

В настоящее время я пишу это в стандартный вывод с stream.pipe(process.stdout, { end: false }), но, как я уже сказал, мне нужны данные потока в виде строки, которые я могу использовать после завершения потока.

Как собрать все данные из потока Node.js в строку?

Ответ 1

Ключ заключается в использовании событий data и end events of a читаемого потока. Послушайте эти события:

stream.on('data', (chunk) => { ... });
stream.on('end', () => { ... });

Когда вы получите событие data, добавьте новый фрагмент данных в буфер, созданный для сбора данных.

Когда вы получите событие end, при необходимости преобразуйте заполненный буфер в строку. Тогда делай, что тебе нужно с этим делать.

Ответ 2

Надеюсь, что это более полезно, чем ответ выше:

var string = '';
stream.on('data',function(data){
  string += data.toString();
  console.log('stream data ' + part);
});

stream.on('end',function(){
  console.log('final output ' + string);
});

Обратите внимание, что конкатенация строк - не самый эффективный способ сбора частей строк, но он используется для простоты (и, возможно, ваш код не заботится об эффективности).

Кроме того, этот код может привести к непредсказуемым ошибкам для текста, не относящегося к ASCII (предполагается, что каждый символ помещается в байте), но, возможно, вам это тоже не важно.

Ответ 3

Ничто из этого не помогло мне. Мне нужно было использовать объект Buffer:

  const chunks = [];

  readStream.on("data", function (chunk) {
    chunks.push(chunk);
  });

  // Send the buffer or you can put it into a var
  readStream.on("end", function () {
    res.send(Buffer.concat(chunks));
  });

Ответ 4

Другим способом будет преобразование потока в обещание (см. Пример ниже) и использование then (или await) для присвоения разрешенного значения переменной.

function streamToString (stream) {
  const chunks = []
  return new Promise((resolve, reject) => {
    stream.on('data', chunk => chunks.push(chunk))
    stream.on('error', reject)
    stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')))
  })
}

const result = await streamToString(stream)

Ответ 5

Я использую обычно эту простую функцию для преобразования потока в строку:

function streamToString(stream, cb) {
  const chunks = [];
  stream.on('data', (chunk) => {
    chunks.push(chunk.toString());
  });
  stream.on('end', () => {
    cb(chunks.join(''));
  });
}

Пример использования:

let stream = fs.createReadStream('./myFile.foo');
streamToString(stream, (data) => {
  console.log(data);  // data is now my string variable
});

Ответ 7

В потоках нет простой функции .toString() (что я понимаю), а не что-то вроде функции .toStringAsync(cb) (которую я не понимаю).

Итак, я создал свою собственную вспомогательную функцию:

var streamToString = function(stream, callback) {
  var str = '';
  stream.on('data', function(chunk) {
    str += chunk;
  });
  stream.on('end', function() {
    callback(str);
  });
}

// how to use:
streamToString(myStream, function(myStr) {
  console.log(myStr);
});

Ответ 8

Мне повезло больше, используя вот так:

let string = '';
readstream
    .on('data', (buf) => string += buf.toString())
    .on('end', () => console.log(string));

Я использую узел v9.11.1 а readstream - это ответ от обратного вызова http.get.

Ответ 9

Простой способ с популярной (более 5 миллионов загрузок в неделю) и легкой библиотекой get-stream:

https://www.npmjs.com/package/get-stream

const fs = require('fs');
const getStream = require('get-stream');

(async () => {
    const stream = fs.createReadStream('unicorn.txt');
    console.log(await getStream(stream)); //output is string
})();

Ответ 10

Как насчет чего-то вроде редуктора потока?

Вот пример использования классов ES6, как использовать его.

var stream = require('stream')

class StreamReducer extends stream.Writable {
  constructor(chunkReducer, initialvalue, cb) {
    super();
    this.reducer = chunkReducer;
    this.accumulator = initialvalue;
    this.cb = cb;
  }
  _write(chunk, enc, next) {
    this.accumulator = this.reducer(this.accumulator, chunk);
    next();
  }
  end() {
    this.cb(null, this.accumulator)
  }
}

// just a test stream
class EmitterStream extends stream.Readable {
  constructor(chunks) {
    super();
    this.chunks = chunks;
  }
  _read() {
    this.chunks.forEach(function (chunk) { 
        this.push(chunk);
    }.bind(this));
    this.push(null);
  }
}

// just transform the strings into buffer as we would get from fs stream or http request stream
(new EmitterStream(
  ["hello ", "world !"]
  .map(function(str) {
     return Buffer.from(str, 'utf8');
  })
)).pipe(new StreamReducer(
  function (acc, v) {
    acc.push(v);
    return acc;
  },
  [],
  function(err, chunks) {
    console.log(Buffer.concat(chunks).toString('utf8'));
  })
);

Ответ 11

Самым чистым решением может быть использование пакета "string-stream", который преобразует поток в строку с обещанием.

const streamString = require('stream-string')

streamString(myStream).then(string_variable => {
    // myStream was converted to a string, and that string is stored in string_variable
    console.log(string_variable)

}).catch(err => {
     // myStream emitted an error event (err), so the promise from stream-string was rejected
    throw err
})

Ответ 12

Это сработало для меня и основано на Node v6.7.0 docs:

let output = '';
stream.on('readable', function() {
    let read = stream.read();
    if (read !== null) {
        // New stream data is available
        output += read.toString();
    } else {
        // Stream is now finished when read is null.
        // You can callback here e.g.:
        callback(null, output);
    }
});

stream.on('error', function(err) {
  callback(err, null);
})

Ответ 13

setEncoding ( 'utf8');

Молодец Себастьян Дж. Выше.

У меня была "проблема с буфером" с несколькими строками тестового кода, которые я имел, и добавил информацию о кодировке, и это решило ее, см. Ниже.

Продемонстрировать проблему

программного обеспечения

// process.stdin.setEncoding('utf8');
process.stdin.on('data', (data) => {
    console.log(typeof(data), data);
});

вход

hello world

выход

object <Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64 0d 0a>

Продемонстрировать решение

программного обеспечения

process.stdin.setEncoding('utf8'); // <- Activate!
process.stdin.on('data', (data) => {
    console.log(typeof(data), data);
});

вход

hello world

выход

string hello world

Ответ 14

Используя довольно популярный пакет stream-buffers , который у вас, вероятно, уже есть в ваших зависимостях проекта, это довольно просто:

// imports
const { WritableStreamBuffer } = require('stream-buffers');
const { promisify } = require('util');
const { createReadStream } = require('fs');
const pipeline = promisify(require('stream').pipeline);

// sample stream
let stream = createReadStream('/etc/hosts');

// pipeline the stream into a buffer, and print the contents when done
let buf = new WritableStreamBuffer();
pipeline(stream, buf).then(() => console.log(buf.getContents().toString()));

Ответ 15

И еще один для строк, использующих обещания:

function getStream(stream) {
  return new Promise(resolve => {
    const chunks = [];

    stream.on("data", chunk => chunks.push(chunk));
    stream.on("end", () => resolve(Buffer.concat(chunks).toString()));
  });
}

Использование:

const stream = fs.createReadStream(__filename);
getStream(stream).then(r=>console.log(r));

удалите .toString() для использования с двоичными данными, если это необходимо.