Back

Escrita de Arquivos em Node.js Explicada: Tudo o que Você Precisa Saber Sobre fs.writeFileSync()

Escrita de Arquivos em Node.js Explicada: Tudo o que Você Precisa Saber Sobre fs.writeFileSync()

Operações com arquivos são uma parte fundamental de muitas aplicações Node.js. Seja criando arquivos de configuração, registrando dados ou gerando relatórios, entender como escrever arquivos de forma eficiente é essencial. O método fs.writeFileSync() oferece uma maneira direta de lidar com operações de escrita síncrona de arquivos no Node.js.

Este guia abrangente cobre tudo o que você precisa saber sobre o uso eficaz de fs.writeFileSync(), desde o uso básico até técnicas avançadas e melhores práticas.

Principais Pontos

  • fs.writeFileSync() escreve dados em arquivos de forma síncrona, bloqueando o loop de eventos até a conclusão
  • Sempre use blocos try-catch para lidar com possíveis erros
  • Use flags apropriadas para controlar o comportamento de criação e escrita de arquivos
  • Considere as implicações de desempenho em aplicações com alta concorrência
  • Para arquivos maiores ou ambientes de produção, considere alternativas assíncronas
  • Esteja atento a questões de segurança, especialmente com caminhos fornecidos pelo usuário

O que é fs.writeFileSync()?

fs.writeFileSync() é um método integrado no módulo de sistema de arquivos (fs) do Node.js que escreve dados em um arquivo de forma síncrona. Diferentemente de sua contraparte assíncrona, este método bloqueia a execução do seu código até que a operação de arquivo seja concluída.

A assinatura do método é:

fs.writeFileSync(file, data[, options])
  • file: Caminho para o arquivo (string, Buffer, URL ou descritor de arquivo)
  • data: Conteúdo a ser escrito (string, Buffer, TypedArray ou DataView)
  • options: Parâmetros de configuração opcionais (string ou objeto)

Uso Básico de fs.writeFileSync()

Escrevendo um Arquivo de Texto Simples

const fs = require('fs');

try {
  fs.writeFileSync('example.txt', 'Hello, Node.js!');
  console.log('Arquivo escrito com sucesso');
} catch (err) {
  console.error('Erro ao escrever arquivo:', err);
}

Este código cria um arquivo chamado example.txt com o conteúdo ""Hello, Node.js!"" no seu diretório de trabalho atual.

Escrevendo Dados JSON

const fs = require('fs');

const user = {
  name: 'John Doe',
  age: 30,
  email: 'john@example.com'
};

try {
  fs.writeFileSync('user.json', JSON.stringify(user, null, 2));
  console.log('Arquivo JSON escrito com sucesso');
} catch (err) {
  console.error('Erro ao escrever arquivo JSON:', err);
}

Entendendo os Parâmetros

O Parâmetro File

O primeiro parâmetro especifica o caminho do arquivo onde os dados serão escritos:

// Escrevendo no diretório atual
fs.writeFileSync('data.txt', 'Algum conteúdo');

// Escrevendo em um caminho específico
fs.writeFileSync('/var/logs/app.log', 'Entrada de log');

// Usando um descritor de arquivo
const fd = fs.openSync('config.json', 'w');
fs.writeFileSync(fd, '{""setting"": ""value""}');
fs.closeSync(fd);

Importante: Se o diretório especificado não existir, o Node.js lançará um erro. O método fs.writeFileSync() não pode criar diretórios.

O Parâmetro Data

O segundo parâmetro contém o conteúdo que você deseja escrever:

// Escrevendo uma string
fs.writeFileSync('file.txt', 'Conteúdo de texto simples');

// Escrevendo um Buffer
const buffer = Buffer.from('Conteúdo binário');
fs.writeFileSync('binary.dat', buffer);

// Escrevendo um TypedArray
const uint8Array = new Uint8Array([72, 101, 108, 108, 111]);  // ""Hello"" em ASCII
fs.writeFileSync('typed-array.txt', uint8Array);

Trabalhando com Buffers

Buffers são particularmente úteis ao lidar com dados binários:

const fs = require('fs');

// Criando um buffer a partir de uma string
const stringData = 'Hello World';
const buffer = Buffer.from(stringData, 'utf8');
fs.writeFileSync('buffer-example.txt', buffer);

// Criando um buffer a partir de JSON
const userData = { name: 'Alice', age: 28 };
const jsonBuffer = Buffer.from(JSON.stringify(userData));
fs.writeFileSync('user-data.json', jsonBuffer);

O Parâmetro Options

O terceiro parâmetro permite personalizar o comportamento de escrita do arquivo:

fs.writeFileSync('config.txt', 'Dados de configuração', {
  encoding: 'utf8',    // Codificação de caracteres (padrão: 'utf8')
  mode: 0o666,         // Permissões do arquivo (padrão: 0o666)
  flag: 'w'            // Flag do sistema de arquivos (padrão: 'w')
});

Opções Comuns de Flag

  • 'w': Abrir para escrita, criar se não existir, truncar se existir (padrão)
  • 'a': Abrir para anexar, criar se não existir
  • 'wx': Como ‘w’, mas falha se o caminho existir
  • 'ax': Como ‘a’, mas falha se o caminho existir
  • 'r+': Abrir para leitura e escrita, o arquivo deve existir
// Anexar a um arquivo em vez de sobrescrevê-lo
fs.writeFileSync('log.txt', 'Nova entrada de logn', { flag: 'a' });

// Criar um novo arquivo apenas se ele não existir
try {
  fs.writeFileSync('config.json', '{}', { flag: 'wx' });
  console.log('Novo arquivo de configuração criado');
} catch (err) {
  if (err.code === 'EEXIST') {
    console.log('Arquivo de configuração já existe');
  } else {
    console.error('Erro:', err);
  }
}

Tratamento de Erros com fs.writeFileSync()

Como fs.writeFileSync() é síncrono, os erros são lançados diretamente. Sempre envolva-o em um bloco try-catch:

try {
  fs.writeFileSync('/path/to/file.txt', 'Conteúdo');
} catch (error) {
  // Lidar com tipos específicos de erro
  if (error.code === 'ENOENT') {
    console.error('Diretório não existe');
  } else if (error.code === 'EACCES') {
    console.error('Permissão negada');
  } else {
    console.error('Erro inesperado:', error);
  }
}

Códigos de Erro Comuns

  • ENOENT: Arquivo ou diretório não existe (frequentemente quando o diretório pai não existe)
  • EACCES: Permissão negada
  • EISDIR: É um diretório (tentando escrever em um diretório)
  • EMFILE: Muitos arquivos abertos
  • EEXIST: Arquivo já existe (quando usando flags ‘wx’ ou ‘ax’)

Casos de Uso Práticos

Criando Arquivos de Configuração

const fs = require('fs');

function createDefaultConfig() {
  const config = {
    apiKey: '',
    debug: false,
    logLevel: 'info',
    maxRetries: 3
  };
  
  try {
    fs.writeFileSync('config.json', JSON.stringify(config, null, 2));
    console.log('Configuração padrão criada');
  } catch (err) {
    console.error('Falha ao criar arquivo de configuração:', err);
  }
}

// Verificar se a configuração existe, criar se não
try {
  fs.accessSync('config.json', fs.constants.F_OK);
  console.log('Arquivo de configuração já existe');
} catch (err) {
  createDefaultConfig();
}

Logging Simples

function logMessage(message) {
  const timestamp = new Date().toISOString();
  const logEntry = `[${timestamp}] ${message}n`;
  
  try {
    fs.writeFileSync('app.log', logEntry, { flag: 'a' });
  } catch (err) {
    console.error('Falha ao escrever no arquivo de log:', err);
  }
}

logMessage('Aplicação iniciada');
// Fazer algo
logMessage('Operação concluída');

Salvando Entrada do Usuário

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

function saveUserData() {
  const username = readline.question('Digite o nome de usuário: ');
  
  if (!username) {
    console.log('Nome de usuário não pode estar vazio');
    return saveUserData();
  }
  
  try {
    const userData = {
      username,
      createdAt: new Date().toISOString()
    };
    
    fs.writeFileSync(`${username}.json`, JSON.stringify(userData, null, 2));
    console.log(`Dados do usuário salvos em ${username}.json`);
  } catch (err) {
    console.error('Falha ao salvar dados do usuário:', err);
  }
}

saveUserData();

Considerações de Desempenho

Operações Síncronas vs. Assíncronas

fs.writeFileSync() bloqueia o loop de eventos até que a operação seja concluída, o que pode impactar o desempenho em aplicações com alta concorrência:

// Síncrono (bloqueia o loop de eventos)
console.time('writeFileSync');
fs.writeFileSync('large-file.txt', 'X'.repeat(1000000));
console.timeEnd('writeFileSync');

// Assíncrono (não bloqueia o loop de eventos)
console.time('writeFile');
fs.writeFile('large-file-async.txt', 'X'.repeat(1000000), () => {
  console.timeEnd('writeFile');
});
console.log('Isso é executado imediatamente enquanto o arquivo está sendo escrito');

Quando Usar fs.writeFileSync()

Use fs.writeFileSync() quando:

  • Estiver escrevendo arquivos pequenos
  • Estiver trabalhando em um script ou ferramenta CLI
  • O arquivo precisar ser escrito antes de continuar a execução
  • Estiver em uma fase de inicialização/configuração

Evite fs.writeFileSync() quando:

  • Trabalhar com arquivos grandes
  • Em servidores web com alta concorrência
  • Em caminhos de código críticos para o desempenho
  • Ao lidar com várias operações de arquivo simultaneamente

Alternativas Modernas

Usando a Sintaxe de Módulos ES

// Sintaxe ESM (requer Node.js 12+)
import { writeFileSync } from 'fs';
import { join } from 'path';

const filePath = join(process.cwd(), 'data.txt');
writeFileSync(filePath, 'Conteúdo usando sintaxe ESM');

Usando a API fs/promises

Para uma abordagem mais moderna com promises mantendo o comportamento síncrono:

// Usando fs/promises com await de nível superior (Node.js 14.8+ com ESM)
import { writeFile } from 'fs/promises';

try {
  // Isso ainda é assíncrono, mas com sintaxe mais limpa
  await writeFile('example.txt', 'Conteúdo com promises');
  console.log('Arquivo escrito com sucesso');
} catch (err) {
  console.error('Erro ao escrever arquivo:', err);
}

Considerações de Segurança

Permissões de Arquivo

Esteja atento às permissões de arquivo ao escrever dados sensíveis:

// Definir permissões restritivas para arquivos sensíveis
fs.writeFileSync('credentials.json', JSON.stringify(credentials), {
  mode: 0o600  // Leitura/escrita apenas para o proprietário
});

Vulnerabilidades de Travessia de Caminho

Sempre valide e sanitize caminhos de arquivo, especialmente quando vêm da entrada do usuário:

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

function safeWriteFile(filename, content) {
  // Sanitizar nome do arquivo para evitar travessia de caminho
  const safeName = path.basename(filename);
  const safePath = path.join('./uploads', safeName);
  
  try {
    fs.writeFileSync(safePath, content);
    return true;
  } catch (err) {
    console.error('Erro ao escrever arquivo:', err);
    return false;
  }
}

Solucionando Problemas Comuns

""ENOENT: no such file or directory""

Isso geralmente significa que o diretório não existe:

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

function writeFileWithDirectoryCreation(filePath, content) {
  const directory = path.dirname(filePath);
  
  try {
    // Criar diretório se não existir
    if (!fs.existsSync(directory)) {
      fs.mkdirSync(directory, { recursive: true });
    }
    
    // Agora escrever o arquivo
    fs.writeFileSync(filePath, content);
    return true;
  } catch (err) {
    console.error('Erro:', err);
    return false;
  }
}

writeFileWithDirectoryCreation('logs/app/data.log', 'Entrada de log');

""EACCES: permission denied""

Verificar permissões de arquivo e diretório:

try {
  fs.writeFileSync('/var/log/app.log', 'Entrada de log');
} catch (err) {
  if (err.code === 'EACCES') {
    // Tentar escrever em um local diferente
    const homeDir = require('os').homedir();
    fs.writeFileSync(path.join(homeDir, 'app.log'), 'Entrada de log');
    console.log('Escrito no diretório home em vez disso');
  }
}

Conclusão

O método fs.writeFileSync() é uma ferramenta poderosa para lidar com operações de arquivo em aplicações Node.js. Ele fornece uma maneira direta de escrever dados em arquivos de forma síncrona, tornando-o ideal para cenários em que você precisa de conclusão garantida antes de continuar a execução. Ao entender seus parâmetros, tratamento de erros e implicações de desempenho, você pode usar esse método efetivamente em seus projetos, evitando armadilhas comuns.

Lembre-se de considerar a natureza síncrona deste método e usá-lo adequadamente com base nas necessidades da sua aplicação. Para aplicações com alta concorrência ou ao trabalhar com arquivos grandes, considere usar alternativas assíncronas como fs.writeFile() ou streams.

FAQs

Use fs.writeFileSync() quando precisar de conclusão garantida antes de continuar a execução, tipicamente em scripts, ferramentas CLI ou código de inicialização. Use fs.writeFile() para a maioria dos outros cenários, especialmente em servidores ou aplicações onde o desempenho é importante.

Use a flag 'a' no parâmetro de opções: fs.writeFileSync('log.txt', 'Nova entradan', { flag: 'a' });

Não, ele só pode criar arquivos. Você precisa usar fs.mkdirSync() para criar diretórios primeiro.

Use objetos Buffer: const buffer = Buffer.from([0x48, 0x65, 0x6c, 0x6c, 0x6f]); // 'Hello' fs.writeFileSync('binary.dat', buffer);

Não há um limite específico na API, mas escrever arquivos muito grandes de forma síncrona pode bloquear o loop de eventos por muito tempo. Para arquivos maiores que alguns MB, considere usar streams ou métodos assíncronos.

Defina a opção mode: fs.writeFileSync('sensitive.txt', 'dados privados', { mode: 0o600 });

Listen to your bugs 🧘, with OpenReplay

See how users use your app and resolve issues fast.
Loved by thousands of developers