Introdução: por que trabalhar com o sistema de arquivos é importante no Node.js
No desenvolvimento web moderno, raramente é possível evitar a interação com o sistema de arquivos. Seja para upload de avatares de usuários, geração de logs, processamento de relatórios CSV ou leitura de arquivos de configuração — o Node.js fornece o módulo nativo e poderoso fs (File System) para essas tarefas.
Diferente do JavaScript do navegador, onde o acesso a arquivos é rigidamente limitado por questões de segurança, o Node.js oferece controle total sobre o sistema de arquivos do servidor. Neste artigo, vamos explorar as operações principais com exemplos práticos que ajudarão você a trabalhar com arquivos e pastas com confiança.
O que você aprenderá:
- Como ler e escrever arquivos (síncrona e assincronamente)
- Como trabalhar com Streams para arquivos grandes
- Como criar, renomear e excluir pastas
- Como lidar corretamente com erros e caminhos
1. Fundamentos: leitura e escrita de arquivos
O módulo fs oferece três abordagens principais para trabalhar com arquivos: síncrona, assíncrona (com callbacks) e baseada em Promises. Recomenda-se usar a abordagem assíncrona com Promises (fs.promises) — ela não bloqueia o Event Loop e é mais legível.
1.1 Lendo um arquivo
A maneira mais simples de ler um arquivo inteiro é usar readFile.
const fs = require('fs').promises;
async function readConfig() { try { const data = await fs.readFile('./config.json', 'utf-8'); const config = JSON.parse(data); console.log('Porta:', config.port); } catch (err) { console.error('Erro de leitura:', err.message); }}
readConfig();Importante: Especifique a codificação ('utf-8'), caso contrário, será retornado um objeto Buffer.
1.2 Escrevendo em um arquivo
Para escrever, use writeFile (sobrescreve o arquivo) ou appendFile (adiciona ao final).
const fs = require('fs').promises;
async function saveLog(message) { const logLine = `[${new Date().toISOString()}] ${message}`; try { await fs.appendFile('./app.log', logLine); console.log('Log salvo'); } catch (err) { console.error('Erro de escrita:', err); }}
saveLog('Servidor iniciado');2. Trabalhando com arquivos grandes: Streams
Imagine que você precisa ler um arquivo de 2 GB. O método readFile carregaria tudo na memória RAM, o que pode causar vazamento de memória. Para esses casos, existem os Streams (fs.createReadStream e fs.createWriteStream).
Um Stream processa os dados em partes (chunks), sem armazenar o arquivo inteiro na memória.
Exemplo: copiando um arquivo grande
const fs = require('fs');const path = require('path');
const source = path.join(__dirname, 'bigfile.zip');const destination = path.join(__dirname, 'copy.zip');
const readStream = fs.createReadStream(source);const writeStream = fs.createWriteStream(destination);
readStream.on('data', (chunk) => { console.log(`Lidos ${chunk.length} bytes`); writeStream.write(chunk);});
readStream.on('end', () => { writeStream.end(); console.log('Cópia concluída');});
readStream.on('error', (err) => { console.error('Erro de leitura:', err);});Dica: Para copiar, é mais conveniente usar o método nativo pipeline do módulo stream — ele lida automaticamente com erros e fecha os streams.
3. Gerenciamento de diretórios
Trabalhar com pastas é uma parte igualmente importante. O Node.js permite criar, ler e excluir diretórios.
3.1 Criando e verificando uma pasta
const fs = require('fs').promises;
async function ensureDir(dirPath) { try { await fs.mkdir(dirPath, { recursive: true }