Модуль Stream Node.js

Java FileВстроенные модули Node.js


Stream — это абстрактный интерфейс в Node.js для обработки потоковых данных. Вы можете представить поток как поток воды, где данные перемещаются из одного места в другое, как вода. Этот подход к обработке особенно подходит для больших файлов или непрерывных источников данных, потому что не требует загружать все данные в память сразу.

Зачем нужны потоки?

  1. Эффективность памяти: При обработке больших файлов нет необходимости загружать весь файл в память сразу
  2. Эффективность по времени: Данные можно обрабатывать по мере их поступления, не дожидаясь готовности всех данных
  3. Композируемость: Несколько потоков можно соединять как трубы, образуя конвейер обработки данных

Четыре основных типа потоков

1. Читаемый поток

Читаемый поток — это источник данных, из которого можно читать данные. Например, чтение содержимого из файла или получение данных из HTTP-запроса.

Пример

const fs = require('fs');
const readableStream = fs.createReadStream('example.txt');

readableStream.on('data', (chunk) => {
  console.log(`Получено ${chunk.length}байт данных`);
});

readableStream.on('end', () => {
  console.log('Больше нет данных');
});

2. Поток для записи

Поток для записи — это место назначения данных, в которое можно записывать данные. Например, запись в файл или отправка HTTP-ответа.

Пример

const fs = require('fs');
const writableStream = fs.createWriteStream('output.txt');

writableStream.write('First line of data\n');
writableStream.write('Second line of data\n');
writableStream.end('Last line of data');

3. Дуплексный поток

Дуплексный поток одновременно поддерживает чтение и запись, например TCP-сокет.

Пример

const { Duplex } = require('stream');

const myDuplex = new Duplex({
  write(chunk, encoding, callback) {
    console.log(chunk.toString());
    callback();
  },
 
  read(size) {
    this.push('Данные, прочитанные из дуплексного потока');
    this.push(null); // Указывает на конец данных
  }
});

4. Поток преобразования

Поток преобразования — это особый тип дуплексного потока, который может изменять или преобразовывать данные в процессе чтения и записи. Например, сжимать или шифровать данные.

Пример

const { Transform } = require('stream');

const upperCaseTr = new Transform({
  transform(chunk, encoding, callback) {
    this.push(chunk.toString().toUpperCase());
    callback();
  }
});

process.stdin.pipe(upperCaseTr).pipe(process.stdout);

Общие методы потоков

Метод pipe()

pipe()Этот метод — одна из самых мощных возможностей Stream; он позволяет соединять несколько потоков вместе, образуя конвейер.

Пример

const fs = require('fs');

// Создать читаемый поток и записываемый поток
const readStream = fs.createReadStream('input.txt');
const writeStream = fs.createWriteStream('output.txt');

// Используйте pipe для соединения читаемого потока и записываемого потока
readStream.pipe(writeStream);

writeStream.on('finish', () => {
  console.log('Data write complete');
});

Прослушивание событий

Stream является экземпляром EventEmitter. Различные ситуации можно обрабатывать, прослушивая события:

  • data- Срабатывает, когда данные доступны для чтения
  • end- Срабатывает, когда больше нет данных для чтения
  • error- Срабатывает при возникновении ошибки
  • finish- Срабатывает, когда все данные сброшены в нижележащую систему

Примеры практического применения

Пример 1: Копирование файла

Пример

const fs = require('fs');

function copyFile(source, target, cb) {
  // Создать потоки чтения и записи
  const rd = fs.createReadStream(source);
  const wr = fs.createWriteStream(target);

  // Обработать ошибки
  rd.on('error', err => cb(err));
  wr.on('error', err => cb(err));
 
  // Обратный вызов завершения
  wr.on('finish', cb);
 
  // Начать копирование
  rd.pipe(wr);
}

// Использование
copyFile('source.txt', 'target.txt', err => {
  if (err) console.error(err);
  else console.log('Копирование завершено');
});

Пример 2: HTTP-сервер, сжимающий файлы

Пример

const fs = require('fs');
const http = require('http');
const zlib = require('zlib');

http.createServer((req, res) => {
  // Создать читаемый поток
  const readStream = fs.createReadStream('./largeFile.txt');
 
  // Установить заголовки ответа
  res.writeHead(200, {
    'Content-Type': 'text/plain',
    'Content-Encoding': 'gzip'
  });
 
  // Цепочка конвейера: чтение -> сжатие -> ответ
  readStream.pipe(zlib.createGzip()).pipe(res);
}).listen(3000);

Лучшие практики работы с потоками

  1. Обработка ошибок: Всегда добавляйте обработчики ошибок к потокам
  2. Управление памятью: Для больших файлов используйте потоки вместо чтения всего сразу
  3. Цепочка конвейера: Используйте метод pipe() должным образом для объединения нескольких потоков
  4. Обработка обратного давления: Когда скорость записи не успевает за скоростью чтения, необходимо правильно обрабатывать обратное давление

Пример обработки ошибок

Пример

const fs = require('fs');

const readStream = fs.createReadStream('not-exist-file.txt');

readStream.on('error', (err) => {
  console.error('Произошла ошибка:', err.message);
});

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

Java FileВстроенные модули Node.js

Другие расширения