Какие есть функции чтения файлов в FS Module?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Функции чтения файлов в 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 кода.