Como anexar a um arquivo no Node?

505

Estou tentando anexar uma seqüência de caracteres a um arquivo de log. No entanto, o writeFile apagará o conteúdo todas as vezes antes de escrever a string.

fs.writeFile('log.txt', 'Hello Node', function (err) {
  if (err) throw err;
  console.log('It\'s saved!');
}); // => message.txt erased, contains only 'Hello Node'

Alguma idéia de como fazer isso da maneira mais fácil?

supercobra
fonte

Respostas:

799

Para anexos ocasionais, você pode usar appendFile , o que cria um novo identificador de arquivo cada vez que é chamado:

Assíncrona :

const fs = require('fs');

fs.appendFile('message.txt', 'data to append', function (err) {
  if (err) throw err;
  console.log('Saved!');
});

Sincronicamente :

const fs = require('fs');

fs.appendFileSync('message.txt', 'data to append');

Mas se você anexar repetidamente ao mesmo arquivo, é muito melhor reutilizar o identificador de arquivo .

denysonique
fonte
6
Desde Node v0.8.0
denysonique
59
Alguém sabe se fs.appendFile mantém um link para o arquivo aberto para que os anexos sejam mais rápidos? (em vez de abrir / fechar cada gravação) nodejs.org/api/…
nelsonic 30/10
7
Caso seja útil: Observe que isso é assíncrono. Isso pode resultar em um tempo estranho e outras coisas. Ex: se você tiver process.exit()logo a seguir fs.appendFile, poderá sair antes que a saída seja enviada. (Usando returné fina.)
SilentSteel
6
Na pior das hipóteses, você pode usar a versão síncrona appendFileSync,. nodejs.org/api/… Mas você pode perder um dos grandes benefícios do Node, que é as operações assíncronas. Certifique-se de capturar erros. Talvez em alguns sistemas operacionais, você possa ter acesso negado se solicitar o identificador de arquivo ao mesmo tempo. Não tenho certeza sobre isso.
SilentSteel 01/12/14
5
@chrisdew Obrigado pela atualização .. mas ... se não quisermos usar a resposta aceita aqui, o que devemos fazer? Como você resolveu esse dilema?
zipzit
215

Quando você deseja gravar em um arquivo de log, ou seja, anexando dados ao final de um arquivo, nunca use appendFile. appendFileabre um identificador de arquivo para cada dado que você adiciona ao seu arquivo, após um tempo você recebe um EMFILEerro bonito .

Posso acrescentar que appendFilenão é mais fácil de usar do que a WriteStream.

Exemplo com appendFile:

console.log(new Date().toISOString());
[...Array(10000)].forEach( function (item,index) {
    fs.appendFile("append.txt", index+ "\n", function (err) {
        if (err) console.log(err);
    });
});
console.log(new Date().toISOString());

Até 8000 no meu computador, você pode anexar dados ao arquivo e obter o seguinte:

{ Error: EMFILE: too many open files, open 'C:\mypath\append.txt'
    at Error (native)
  errno: -4066,
  code: 'EMFILE',
  syscall: 'open',
  path: 'C:\\mypath\\append.txt' }

Além disso, appendFile gravará quando estiver ativado, para que seus logs não sejam gravados com carimbo de data e hora. Você pode testar, por exemplo, definir 1000 no lugar de 100000, a ordem será aleatória, depende do acesso ao arquivo.

Se você deseja anexar a um arquivo, deve usar um fluxo gravável como este:

var stream = fs.createWriteStream("append.txt", {flags:'a'});
console.log(new Date().toISOString());
[...Array(10000)].forEach( function (item,index) {
    stream.write(index + "\n");
});
console.log(new Date().toISOString());
stream.end();

Você termina quando quiser. Você nem precisa usar stream.end(), a opção padrão é que AutoClose:true, assim, seu arquivo será encerrado quando seu processo terminar e você evitará abrir muitos arquivos.

Plaute
fonte
2
Este é o local.
Pogrindis 27/03
3
Obrigado pela ótima resposta, mas a minha dúvida é que, devido à natureza assíncrona do Javascript, ele será executado stream.end()antes do stream.write(), portanto, não devemos usar stream.end(), também como você mencionou que AutoClose:Trueé uma opção padrão, por que se preocupar em escrever uma linha que não seja usar.
Aashish Kumar #
13
due to asynchronous nature of Javascript... O que? Array.forEach é uma operação síncrona. JS é síncrono. Apenas fornece algumas maneiras de gerenciar operações assíncronas, como Promises e async / waitit.
Sharcoux
1
Eu acho que fs.appendFileresulta em muitos arquivos abertos porque você o executa de maneira assíncrona (você está criando assincronamente 10000 arquivos), acredito appendFileSyncque não teria problema semelhante, também não fs.appendFilecom intervalo adequado (1s provavelmente é mais do que suficiente) ou na fila.
apple apple
1
@RedwolfPrograms Para o registro de servidor ocupado, talvez seja verdade. Para um registro único por execução, talvez não. De qualquer forma, apenas afirmo que o ponto (pelo menos o motivo) nesta resposta não está correto.
apple apple
122

Seu código usando createWriteStream cria um descritor de arquivo para cada gravação. log.end é melhor porque pede que o nó feche imediatamente após a gravação.

var fs = require('fs');
var logStream = fs.createWriteStream('log.txt', {flags: 'a'});
// use {flags: 'a'} to append and {flags: 'w'} to erase and write a new file
logStream.write('Initial line...');
logStream.end('this is the end line');
Fabio Ferrari
fonte
6
faltando primeira linha! deve ser 'var fs = require (' fs ');'
Stormbytes
5
Ou talvez ainda melhor var fs = require('graceful-fs'), que solucionou alguns problemas conhecidos. Veja os documentos para mais informações.
Marko Bonaci 27/05
3
A linha inicial e a linha final estão na mesma linha :-p
binki
5
Observe : Se você estiver usando fs.createWriteStream, use flags. Se você estiver usando, fs.writeFileentão é flag. Consulte Nó JS Docs - Sistema de arquivos para obter mais informações.
Anish Nair
2
Seja cuidadoso! O parâmetro não é "sinalizadores", mas "sinalizador" (singular): nodejs.org/api/…
Benny Neugebauer
25

Além disso appendFile, você também pode passar uma bandeira writeFilepara acrescentar dados a um arquivo existente.

fs.writeFile('log.txt', 'Hello Node',  {'flag':'a'},  function(err) {
    if (err) {
        return console.error(err);
    }
});

Ao passar o sinalizador 'a', os dados serão anexados no final do arquivo.

AJ 9
fonte
3
Observe : Se você estiver usando fs.createWriteStream, use flags. Se você estiver usando, fs.writeFileentão é flag. Consulte Nó JS Docs - Sistema de arquivos para obter mais informações.
Anish Nair
19

Você precisa abri-lo e depois escrever nele.

var fs = require('fs'), str = 'string to append to file';
fs.open('filepath', 'a', 666, function( e, id ) {
  fs.write( id, 'string to append to file', null, 'utf8', function(){
    fs.close(id, function(){
      console.log('file closed');
    });
  });
});

Aqui estão alguns links que ajudarão a explicar os parâmetros

abrir
escrever
fechar


EDIT : esta resposta não é mais válida, procure o novo método fs.appendFile para anexar.

Corey Hart
fonte
1
parecido com o supercobra constantemente grava o log no arquivo de log, o uso de fs.write não é recomendado nesse caso; use fs.createWriteStream. Leia nodejs.org/docs/v0.4.8/api/all.html#fs.write
angry kiwi
10
A resposta não é mais precisa a partir do nodejs v0.4.10.
precisa saber é o seguinte
deve ser '0666' em vez de 666.
Ya Zhuang
13

O Node.js 0.8 possui fs.appendFile:

fs.appendFile('message.txt', 'data to append', (err) => {
  if (err) throw err;
  console.log('The "data to append" was appended to file!');
});

Documentação

chbrown
fonte
3
fd = fs.openSync(path.join(process.cwd(), 'log.txt'), 'a')
fs.writeSync(fd, 'contents to append')
fs.closeSync(fd)
Luis R.
fonte
5
qualquer coisa sync () é quase sempre uma má ideia, a menos que você esteja 100% certo de que NECESSITA absolutamente. Mesmo assim, você provavelmente está fazendo errado.
Zane Claes
4
Não significa que está errado. Apenas faz isso de forma síncrona. Pode não ser a melhor prática para o Node.js, mas é suportado.
Luis R.
2
Eu estava usando "você está fazendo errado" no sentido coloquial de memes da Internet. Obviamente, ele é suportado = P
Zane Claes
7
Concordou em assíncrono, mas às vezes, se você está apenas escrevendo um script interativo, a sincronização é boa.
Brianmac # 13/14
7
Escrever de forma síncrona é absolutamente aceitável se você estiver usando um aplicativo de linha de comando de usuário único (por exemplo, script para fazer algumas coisas). Dessa forma, é mais rápido fazer coisas. Por que o nó teria métodos de sincronização se não fosse para esse fim?
Jan Święcki 16/05
3

Se você deseja uma maneira fácil e sem estresse de escrever registros linha por linha em um arquivo, recomendo o fs-extra :

const os = require('os');
const fs = require('fs-extra');

const file = 'logfile.txt';
const options = {flag: 'a'};

async function writeToFile(text) {
  await fs.outputFile(file, `${text}${os.EOL}`, options);
}

writeToFile('First line');
writeToFile('Second line');
writeToFile('Third line');
writeToFile('Fourth line');
writeToFile('Fifth line');

Testado com o nó v8.9.4.

Benny Neugebauer
fonte
2

Minha abordagem é bastante especial. Basicamente, uso a WriteStreamsolução, mas sem 'fechar' o fd usando stream.end(). Em vez disso, eu uso cork/uncork . Isso tem o benefício do baixo uso de RAM (se isso for importante para alguém) e acredito que é mais seguro usar para log / gravação (meu caso de uso original).

A seguir, é apresentado um exemplo bastante simples. Observe que acabei de adicionar um forloop pseudo para showcase - no código de produção, estou aguardando mensagens do websocket.

var stream = fs.createWriteStream("log.txt", {flags:'a'});
for(true) {
  stream.cork();
  stream.write("some content to log");
  process.nextTick(() => stream.uncork());
}

uncork liberará os dados para o arquivo no próximo tick.

No meu cenário, existem picos de até ~ 200 gravações por segundo em vários tamanhos. Durante a noite, porém, são necessárias apenas algumas gravações por minuto. O código está funcionando super confiável mesmo nos horários de pico.

Tom-Oliver Heidel
fonte
1

Eu ofereço essa sugestão apenas porque o controle sobre os sinalizadores abertos às vezes é útil; por exemplo, você pode truncar primeiro um arquivo existente e depois anexar uma série de gravações a ele - nesse caso, use o sinalizador 'w' ao abrir o arquivo e não feche até que todas as gravações estejam concluídas. É claro que appendFile pode ser o que você procura :-)

  fs.open('log.txt', 'a', function(err, log) {
    if (err) throw err;
    fs.writeFile(log, 'Hello Node', function (err) {
      if (err) throw err;
      fs.close(log, function(err) {
        if (err) throw err;
        console.log('It\'s saved!');
      });
    });
  });
balrob
fonte
1

Usando o pacote jfile :

myFile.text+='\nThis is new line to be appended'; //myFile=new JFile(path);
Abdennour TOUMI
fonte
1

Usar fs.appendFileou fsPromises.appendFilesão as opções mais rápidas e robustas quando você precisa acrescentar algo a um arquivo.

Ao contrário de algumas das respostas sugeridas, se o caminho do arquivo é fornecido para a appendFilefunção, ele realmente fecha sozinho . Somente quando você passa um identificador de arquivo que obtém algo como fs.open()você precisa cuidar para fechá-lo.

Eu tentei com mais de 50.000 linhas em um arquivo.

Exemplos :

(async () => {
  // using appendFile.
  const fsp = require('fs').promises;
  await fsp.appendFile(
    '/path/to/file', '\r\nHello world.'
  );

  // using apickfs; handles error and edge cases better.
  const apickFileStorage = require('apickfs');
  await apickFileStorage.writeLines(
    '/path/to/directory/', 'filename', 'Hello world.'
  );
})();

insira a descrição da imagem aqui

Ref: https://github.com/nodejs/node/issues/7560
Exemplo de execução: https://github.com/apickjs/apickFS/blob/master/writeLines.js

vivek agarwal
fonte
0

Aqui está um script completo. Preencha os nomes dos arquivos e execute-os e ele deve funcionar! Aqui está um tutorial em vídeo sobre a lógica por trás do script.

var fs = require('fs');

function ReadAppend(file, appendFile){
  fs.readFile(appendFile, function (err, data) {
    if (err) throw err;
    console.log('File was read');

    fs.appendFile(file, data, function (err) {
      if (err) throw err;
      console.log('The "data to append" was appended to file!');

    });
  });
}
// edit this with your file names
file = 'name_of_main_file.csv';
appendFile = 'name_of_second_file_to_combine.csv';
ReadAppend(file, appendFile);
Jvieitez
fonte
0

uma maneira mais fácil de fazer isso é

const fs = require('fs');
fs.appendFileSync('file.txt', 'message to append into file');
Adeojo Emmanuel IMM
fonte
Mais fácil do que o que? Você verificou a resposta principal, que oferece exatamente a mesma appendFileSyncsolução?
Dan Dascalescu 23/06/19
0
const inovioLogger = (logger = "") => {
    const log_file = fs.createWriteStream(__dirname + `/../../inoviopay-${new Date().toISOString().slice(0, 10)}.log`, { flags: 'a' });
    const log_stdout = process.stdout;
    log_file.write(logger + '\n');
}
sunilsingh
fonte
0

Além da resposta da denysonique , às vezes appendFilesão utilizados tipos assíncronos e outros métodos assíncronos no NodeJS, nos quais a promessa retorna em vez da passagem de retorno de chamada. Para isso, é necessário agrupar a função com promisifyHOF ou importar funções assíncronas do namespace promises:

const { appendFile } = require('fs').promises;

await appendFile('path/to/file/to/append', dataToAppend, optionalOptions);

Espero que ajude 😉

Alex Gusev
fonte