← Назад к вопросам

В чем разница между readFile и readStream?

1.7 Middle🔥 151 комментариев
#Node.js и JavaScript#Кэширование и производительность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

В чем разница между readFile и readStream

Краткий ответ

readFile загружает весь файл в память, а readStream читает файл по частям (chunks). readFile проще, но неэффективен для больших файлов. readStream экономит память, но требует обработки событий.

readFile — весь файл в память

const fs = require('fs');

// Асинхронная версия
fs.readFile('./large-file.txt', 'utf8', (err, data) => {
  if (err) throw err;
  console.log(data); // Весь файл в памяти
});

// Promise версия
const fs = require('fs').promises;
const data = await fs.readFile('./file.txt', 'utf8');
console.log(data);

Характеристики:

  • Весь файл загружается в памяти
  • Просто в использовании
  • Блокирует выполнение (асинхронно, но весь файл ждёт)
  • Подходит для маленьких файлов
  • Высокое потребление RAM для больших файлов
readFile поток загрузки:
┌─────────────┐
│   Start     │
│  Loading    │
│ Entire File │ ← весь файл в RAM
│   Ready     │
└─────────────┘
(1 callback)

readStream — читаем частями

const fs = require('fs');

const stream = fs.createReadStream('./large-file.txt', {
  encoding: 'utf8',
  highWaterMark: 16 * 1024 // 16 KB за раз (по умолчанию)
});

stream.on('data', (chunk) => {
  console.log('Received chunk:', chunk.length, 'bytes');
});

stream.on('end', () => {
  console.log('File reading complete');
});

stream.on('error', (err) => {
  console.error('Stream error:', err);
});

Характеристики:

  • Читает файл по частям (chunks)
  • Экономит память
  • События: data, end, error, close
  • Подходит для больших файлов
  • Немного сложнее в использовании
readStream поток загрузки:
┌─────┬─────┬─────┬─────┬─────┐
│Ch 1 │Ch 2 │Ch 3 │Ch 4 │Ch 5 │ ← chunks в буфере
└─────┴─────┴─────┴─────┴─────┘
(много data событий)

Сравнение использования памяти

// Файл 1 GB

// ❌ readFile — RAM spike до 1 GB
fs.readFile('./1gb-file.bin', (err, data) => {
  // Память: +1 GB
  process(data);
  // Память: -1 GB (после сборки мусора)
});

// ✅ readStream — RAM spike только ~16 KB
fs.createReadStream('./1gb-file.bin')
  .on('data', (chunk) => {
    // Память: +16 KB
    process(chunk);
    // Память: -16 KB (следующий chunk заменит этот)
  });

Практические примеры

1. Отправка файла клиенту (Express)

// ❌ Плохо — readFile
app.get('/download', (req, res) => {
  fs.readFile('./large-file.zip', (err, data) => {
    res.send(data); // Весь файл в памяти
  });
});

// ✅ Хорошо — pipe
app.get('/download', (req, res) => {
  fs.createReadStream('./large-file.zip')
    .pipe(res); // Стриминг напрямую
});

2. Обработка логов

const readline = require('readline');
const fs = require('fs');

const rl = readline.createInterface({
  input: fs.createReadStream('./huge-log.txt'),
  crlfDelay: Infinity
});

rl.on('line', (line) => {
  if (line.includes('ERROR')) {
    console.log(line);
  }
});

rl.on('close', () => {
  console.log('Log processed');
});

3. Копирование файла

// ❌ readFile — неэффективно
fs.readFile('./source.bin', (err, data) => {
  fs.writeFile('./dest.bin', data, (err) => {
    console.log('Done');
  });
});

// ✅ readStream + writeStream — оптимально
fs.createReadStream('./source.bin')
  .pipe(fs.createWriteStream('./dest.bin'))
  .on('close', () => {
    console.log('Copied');
  });

4. Стриминг видео/музыки

app.get('/video/:id', (req, res) => {
  const filePath = `./videos/${req.params.id}.mp4`;
  const stat = fs.statSync(filePath);
  const fileSize = stat.size;
  const range = req.headers.range;

  if (range) {
    const parts = range.replace(/bytes=/, '').split('-');
    const start = parseInt(parts[0], 10);
    const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;
    const chunksize = end - start + 1;

    res.writeHead(206, {
      'Content-Range': `bytes ${start}-${end}/${fileSize}`,
      'Accept-Ranges': 'bytes',
      'Content-Length': chunksize,
      'Content-Type': 'video/mp4'
    });

    fs.createReadStream(filePath, { start, end })
      .pipe(res);
  } else {
    res.writeHead(200, {
      'Content-Length': fileSize,
      'Content-Type': 'video/mp4'
    });
    fs.createReadStream(filePath).pipe(res);
  }
});

5. Обработка CSV (transform stream)

const { Transform } = require('stream');
const csv = require('csv-parser');

fs.createReadStream('./data.csv')
  .pipe(csv())
  .on('data', (row) => {
    console.log('Processing row:', row);
  })
  .on('end', () => {
    console.log('CSV processing complete');
  });

Когда использовать

readFile:

  • Маленькие текстовые файлы (< 10 MB)
  • Конфигурационные файлы
  • JSON/YAML
  • Когда нужен весь контент сразу

readStream:

  • Большие файлы (> 100 MB)
  • Стриминг (видео, аудио)
  • Обработка логов
  • Real-time обработка
  • Передача файлов по сети

Вывод

  • readFile — прост, но опасен для больших файлов
  • readStream — экономит память, лучше для production
  • pipe() — красивый способ соединить read и write потоки
  • Для большинства production задач используй streams
В чем разница между readFile и readStream? | PrepBro