Модуль Stream Node.js
Stream — это абстрактный интерфейс в Node.js для обработки потоковых данных. Вы можете представить поток как поток воды, где данные перемещаются из одного места в другое, как вода. Этот подход к обработке особенно подходит для больших файлов или непрерывных источников данных, потому что не требует загружать все данные в память сразу.
Зачем нужны потоки?
- Эффективность памяти: При обработке больших файлов нет необходимости загружать весь файл в память сразу
- Эффективность по времени: Данные можно обрабатывать по мере их поступления, не дожидаясь готовности всех данных
- Композируемость: Несколько потоков можно соединять как трубы, образуя конвейер обработки данных
Четыре основных типа потоков
1. Читаемый поток
Читаемый поток — это источник данных, из которого можно читать данные. Например, чтение содержимого из файла или получение данных из HTTP-запроса.
Пример
const readableStream = fs.createReadStream('example.txt');
readableStream.on('data', (chunk) => {
console.log(`Получено ${chunk.length}байт данных`);
});
readableStream.on('end', () => {
console.log('Больше нет данных');
});
2. Поток для записи
Поток для записи — это место назначения данных, в которое можно записывать данные. Например, запись в файл или отправка HTTP-ответа.
Пример
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 myDuplex = new Duplex({
write(chunk, encoding, callback) {
console.log(chunk.toString());
callback();
},
read(size) {
this.push('Данные, прочитанные из дуплексного потока');
this.push(null); // Указывает на конец данных
}
});
4. Поток преобразования
Поток преобразования — это особый тип дуплексного потока, который может изменять или преобразовывать данные в процессе чтения и записи. Например, сжимать или шифровать данные.
Пример
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 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: Копирование файла
Пример
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 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);
Лучшие практики работы с потоками
- Обработка ошибок: Всегда добавляйте обработчики ошибок к потокам
- Управление памятью: Для больших файлов используйте потоки вместо чтения всего сразу
- Цепочка конвейера: Используйте метод pipe() должным образом для объединения нескольких потоков
- Обработка обратного давления: Когда скорость записи не успевает за скоростью чтения, необходимо правильно обрабатывать обратное давление
Пример обработки ошибок
Пример
const readStream = fs.createReadStream('not-exist-file.txt');
readStream.on('error', (err) => {
console.error('Произошла ошибка:', err.message);
});
Освоив модуль Stream в Node.js, вы сможете эффективно выполнять различные операции ввода-вывода. Особенно при обработке больших файлов или данных в реальном времени потоки станут вашим мощным инструментом.
Другие расширения
Встроенные модули Node.js