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

Какие есть функции чтения файлов в FS Module?

1.0 Junior🔥 211 комментариев
#Node.js и JavaScript

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

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

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

Функции чтения файлов в FS Module

FS (File System) модуль в Node.js предоставляет API для работы с файловой системой. Для чтения файлов есть несколько функций, каждая с разными сценариями использования.

Основные функции чтения

1. fs.readFile() — Асинхронное чтение всего файла

readFile загружает весь файл в память и возвращает содержимое.

const fs = require('fs');

// Callback-based (старый способ)
fs.readFile('./data.txt', 'utf8', (err, data) => {
  if (err) {
    console.error('Error reading file:', err);
    return;
  }
  console.log(data);
});

// Promise-based (modern способ)
const fs = require('fs').promises;

async function readData() {
  try {
    const data = await fs.readFile('./data.txt', 'utf8');
    console.log(data);
  } catch (err) {
    console.error('Error reading file:', err);
  }
}

readData();

Кодировки (encodings):

// UTF-8 (текст)
await fs.readFile('./text.txt', 'utf8');

// Binary (bytes)
await fs.readFile('./image.png'); // Возвращает Buffer

// Latin1, ASCII, UTF-16LE и другие
await fs.readFile('./file.txt', 'latin1');

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

  • Маленькие файлы (< 10 MB)
  • Нужно всё содержимое сразу

Потенциальная проблема — Memory Issue:

// ❌ Плохо — если файл 1 GB, загрузим 1 GB в RAM
await fs.readFile('./huge-file.txt', 'utf8');

// ✅ Хорошо — используем streaming для больших файлов

2. fs.readFileSync() — Синхронное чтение

readFileSync блокирует event loop до завершения.

const fs = require('fs');

// Блокирующее чтение
const data = fs.readFileSync('./config.json', 'utf8');
const config = JSON.parse(data);

console.log(config);

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

  • Только при инициализации (startup) приложения
  • Никогда не используй в request handlers!
// ❌ ПЛОХО — блокирует все incoming requests!
app.get('/api/config', (req, res) => {
  const config = fs.readFileSync('./config.json', 'utf8');
  res.json(JSON.parse(config));
});

// ✅ ХОРОШО — читаем один раз при старте
let appConfig;
app.listen(3000, () => {
  appConfig = JSON.parse(fs.readFileSync('./config.json', 'utf8'));
  console.log('Config loaded');
});

app.get('/api/config', (req, res) => {
  res.json(appConfig); // Уже в памяти
});

3. fs.createReadStream() — Потоковое чтение (РЕКОМЕНДУЕТСЯ для больших файлов)

Stream читает файл порциями (chunks), не загружая всё в память.

const fs = require('fs');

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

let data = '';

stream.on('data', (chunk) => {
  console.log(`Received ${chunk.length} bytes of data`);
  data += chunk;
});

stream.on('end', () => {
  console.log('Stream ended, total length:', data.length);
});

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

Stream для обработки больших файлов:

// Пример: Обработать 1GB лог файл
const fs = require('fs');
const readline = require('readline');

const stream = fs.createReadStream('./huge.log');

const rl = readline.createInterface({
  input: stream,
  crlfDelay: Infinity
});

let errorCount = 0;

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

rl.on('close', () => {
  console.log(`Total errors: ${errorCount}`);
});

// Вместо загрузки 1GB в память, читаем по строкам
// Memory usage: ~10 MB (размер одной строки)

Piping (трубопровод):

// Копировать файл через pipe
fs.createReadStream('./source.txt')
  .pipe(fs.createWriteStream('./destination.txt'));

// Скомпрессировать файл
const fs = require('fs');
const zlib = require('zlib');

fs.createReadStream('./input.txt')
  .pipe(zlib.createGzip())
  .pipe(fs.createWriteStream('./input.txt.gz'));

// Обработать HTTP response
const http = require('http');

http.createServer((req, res) => {
  const stream = fs.createReadStream('./large-file.pdf');
  res.writeHead(200, { 'Content-Type': 'application/pdf' });
  stream.pipe(res); // Stream прямо в HTTP response
}).listen(3000);

4. fs.open() и fs.read() — Низкоуровневое чтение

Для полного контроля над процессом чтения.

const fs = require('fs').promises;

async function readPartOfFile() {
  // Открыть файл
  const fileHandle = await fs.open('./data.bin', 'r');
  
  try {
    // Читаем ровно 100 байт начиная с позиции 1000
    const buffer = Buffer.alloc(100);
    const { bytesRead, buffer: readBuffer } = await fileHandle.read(
      buffer,
      0,           // offset в buffer
      100,         // length to read
      1000         // position в файле
    );
    
    console.log(`Read ${bytesRead} bytes`);
    console.log(readBuffer.toString('utf8'));
  } finally {
    await fileHandle.close();
  }
}

readPartOfFile();

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

  • Чтение определённой части большого файла
  • Binary форматы (images, videos)
  • Когда нужен максимальный контроль

5. fs.readdir() — Чтение содержимого директории

const fs = require('fs').promises;

// Список файлов в директории
async function listFiles() {
  try {
    const files = await fs.readdir('./uploads');
    console.log(files);
    // ['file1.txt', 'file2.jpg', 'folder']
  } catch (err) {
    console.error('Error reading directory:', err);
  }
}

// С информацией о файлах
async function listFilesWithDetails() {
  try {
    const files = await fs.readdir('./uploads', { withFileTypes: true });
    
    for (const file of files) {
      console.log(`${file.name} - ${file.isDirectory() ? 'DIR' : 'FILE'}`);
    }
  } catch (err) {
    console.error('Error:', err);
  }
}

listFilesWithDetails();

6. fs.stat() — Информация о файле

const fs = require('fs').promises;

async function getFileInfo() {
  try {
    const stats = await fs.stat('./data.json');
    
    console.log(`File size: ${stats.size} bytes`);
    console.log(`Created: ${stats.birthtime}`);
    console.log(`Modified: ${stats.mtime}`);
    console.log(`Is file: ${stats.isFile()}`);
    console.log(`Is directory: ${stats.isDirectory()}`);
  } catch (err) {
    console.error('Error:', err);
  }
}

getFileInfo();

7. fs.watch() и fs.watchFile() — Мониторинг изменений

const fs = require('fs');

// Более современный способ (рекомендуется)
fs.watch('./config.json', (eventType, filename) => {
  if (eventType === 'change') {
    console.log(`Config file changed: ${filename}`);
    // Перезагрузить конфиг
  }
});

// Старый способ (watchFile)
fs.watchFile('./data.txt', (curr, prev) => {
  if (curr.mtime > prev.mtime) {
    console.log('File was modified');
  }
});

Сравнение методов

Метод              Размер файла    Применение
────────────────────────────────────────────────
readFile()         < 10 MB         Конфиги, JSON, маленькие файлы
readFileSync()     < 10 MB         Только при инициализации
readStream()       > 10 MB         Логи, бэкапы, большие данные
open/read          Любой           Binary форматы, partial read
readdir()          -               Список файлов
stat()             -               Информация о файле

Практические примеры для backend

Чтение JSON конфига

// config.ts
import { promises as fs } from 'fs';

interface AppConfig {
  port: number;
  database: string;
  apiKey: string;
}

let config: AppConfig;

export async function loadConfig(): Promise<void> {
  try {
    const data = await fs.readFile('./config.json', 'utf8');
    config = JSON.parse(data);
  } catch (err) {
    throw new Error(`Failed to load config: ${err.message}`);
  }
}

export function getConfig(): AppConfig {
  if (!config) {
    throw new Error('Config not loaded. Call loadConfig() first');
  }
  return config;
}

Обработка больших CSV файлов

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

async function processCSV() {
  const stream = fs.createReadStream('./users.csv');
  const rl = readline.createInterface({
    input: stream,
    crlfDelay: Infinity
  });

  let lineNumber = 0;
  const users = [];

  for await (const line of rl) {
    if (lineNumber === 0) {
      // Skip header
      lineNumber++;
      continue;
    }

    const [id, name, email] = line.split(',');
    users.push({ id, name, email });
    
    // Batch insert every 1000 rows
    if (users.length === 1000) {
      await saveToDatabase(users);
      users.length = 0;
    }
    
    lineNumber++;
  }

  // Save remaining
  if (users.length > 0) {
    await saveToDatabase(users);
  }
}

processCSV();

Скачивание файла через API

import express from 'express';
import { createReadStream } from 'fs';

const app = express();

app.get('/download/:filename', (req, res) => {
  const filepath = `./uploads/${req.params.filename}`;
  
  // Проверяем, что файл существует
  try {
    const stream = createReadStream(filepath);
    
    res.setHeader('Content-Disposition', `attachment; filename="${req.params.filename}"`);
    res.setHeader('Content-Type', 'application/octet-stream');
    
    // Stream файл в response
    stream.pipe(res);
    
    // Обработать ошибки
    stream.on('error', (err) => {
      if (err.code === 'ENOENT') {
        res.status(404).json({ error: 'File not found' });
      } else {
        res.status(500).json({ error: 'Internal error' });
      }
    });
  } catch (err) {
    res.status(500).json({ error: 'Error reading file' });
  }
});

Лучшие практики

1. Всегда используй async/await, не callbacks

// ❌ Callback hell
fs.readFile('./a.txt', (err, data) => {
  if (err) throw err;
  fs.readFile('./b.txt', (err, data2) => {
    if (err) throw err;
    // ...
  });
});

// ✅ Async/await
async function readBoth() {
  try {
    const a = await fs.readFile('./a.txt');
    const b = await fs.readFile('./b.txt');
    return { a, b };
  } catch (err) {
    console.error(err);
  }
}

2. Для больших файлов ВСЕГДА используй Stream

// ❌ Плохо для 100 MB файла
const huge = await fs.readFile('./huge.zip'); // 100 MB в памяти!

// ✅ Хорошо
fs.createReadStream('./huge.zip').pipe(res);

3. Обработай ошибки правильно

const fs = require('fs').promises;

try {
  const data = await fs.readFile('./file.txt');
} catch (err) {
  if (err.code === 'ENOENT') {
    console.error('File not found');
  } else if (err.code === 'EACCES') {
    console.error('Permission denied');
  } else {
    console.error('Unknown error:', err);
  }
}

4. Установи highWaterMark для контроля memory

const stream = fs.createReadStream('./file.txt', {
  highWaterMark: 256 * 1024 // 256 KB chunks вместо 16 KB
});

Понимание различных способов чтения файлов — ключ к написанию эффективного и масштабируемого backend кода.