Comment ajouter à un fichier dans Node?

505

J'essaie d' ajouter une chaîne à un fichier journal. Cependant writeFile effacera le contenu à chaque fois avant d'écrire la chaîne.

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

Une idée de comment le faire facilement?

supercobra
la source

Réponses:

799

Pour les ajouts occasionnels, vous pouvez utiliser appendFile, ce qui crée un nouveau descripteur de fichier chaque fois qu'il est appelé:

De manière asynchrone :

const fs = require('fs');

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

Synchrone :

const fs = require('fs');

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

Mais si vous ajoutez plusieurs fois au même fichier, il est préférable de réutiliser le descripteur de fichier .

denysonique
la source
6
Depuis Node v0.8.0
denysonique
59
Est-ce que quelqu'un sait si fs.appendFile garde un lien vers le fichier ouvert afin que les ajouts soient plus rapides? (plutôt que d'ouvrir / fermer chaque écriture) nodejs.org/api/…
nelsonic
7
Dans le cas où c'est pratique: notez que c'est asynchrone. Cela peut entraîner un timing étrange et d'autres choses. Ex: si vous avez process.exit()juste après fs.appendFile, vous pouvez quitter avant d'envoyer la sortie. (L'utilisation returnest très bien.)
SilentSteel
6
Pire cas, vous pouvez utiliser la version synchrone, appendFileSync. nodejs.org/api/… Mais vous risquez de perdre l'un des grands avantages de Node, qui est les opérations asynchrones. Assurez-vous de détecter les erreurs. Peut-être que sur certains systèmes d'exploitation, l'accès peut être refusé si vous demandez le descripteur de fichier en même temps. Je n'en suis pas sûr.
SilentSteel du
5
@chrisdew Merci pour la mise à jour .. mais ... si nous ne devons pas utiliser la réponse acceptée ici, que sommes-nous censés faire? Comment avez-vous résolu ce dilemme?
zipzit
215

Lorsque vous souhaitez écrire dans un fichier journal, c'est-à-dire ajouter des données à la fin d'un fichier, n'utilisez jamaisappendFile . appendFileouvre un descripteur de fichier pour chaque élément de données que vous ajoutez à votre fichier, après un certain temps, vous obtenez une belle EMFILEerreur.

Je peux ajouter que ce appendFilen'est pas plus facile à utiliser qu'un WriteStream.

Exemple avec 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());

Jusqu'à 8000 sur mon ordinateur, vous pouvez ajouter des données au fichier, puis vous obtenez ceci:

{ 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' }

De plus, appendFileécrit quand il est activé, donc vos journaux ne seront pas écrits par horodatage. Vous pouvez tester avec un exemple, définir 1000 au lieu de 100000, l'ordre sera aléatoire, dépend de l'accès au fichier.

Si vous souhaitez ajouter à un fichier, vous devez utiliser un flux inscriptible comme celui-ci:

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();

Vous y mettez fin quand vous voulez. Vous n'êtes même pas obligé d'utiliser stream.end(), l'option par défaut est AutoClose:true, donc votre fichier se terminera à la fin de votre processus et vous éviterez d'ouvrir trop de fichiers.

Plaute
la source
2
C'est parfait.
Pogrindis
3
Merci pour la bonne réponse, mais mon doute est qu'en raison de la nature asynchrone de Javascript, il s'exécutera stream.end()avant le stream.write(), donc nous ne devrions pas utiliser stream.end(), également comme vous l'avez mentionné, c'est AutoClose:Trueune option par défaut, alors pourquoi prendre la peine d'écrire une ligne qui n'est pas utilisation.
Aashish Kumar
13
due to asynchronous nature of Javascript... Quoi? Array.forEach est une opération synchrone. JS est synchrone. Il se trouve que cela fournit des moyens de gérer les opérations asynchrones, comme Promises et async / wait.
Sharcoux
1
Je suppose que cela fs.appendFileentraînerait trop de fichiers ouverts parce que vous l'exécutez de manière asynchrone (vous créez simplement un descripteur de fichier 10000 de manière asynchrone), je pense appendFileSyncqu'il n'y aurait pas de problème similaire, pas plus fs.appendFileavec un intervalle approprié (1 s est probablement plus que suffisant) ou faire la queue.
pomme pomme
1
@RedwolfPrograms Pour le journal de serveur occupé, peut-être vrai. Pour une seule fois par journal d'exécution, peut-être pas. Quoi qu'il en soit, je déclare simplement que le point (au moins la raison) de cette réponse n'est pas correct.
apple apple
122

Votre code utilisant createWriteStream crée un descripteur de fichier pour chaque écriture. log.end est meilleur car il demande au nœud de se fermer immédiatement après l'écriture.

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
la source
6
première ligne manquante! devrait être 'var fs = require (' fs ');'
Stormbytes
5
Ou peut-être encore mieux var fs = require('graceful-fs'), ce qui a permis de résoudre certains problèmes connus. Voir la documentation pour plus d'informations.
Marko Bonaci
3
La ligne initiale et la ligne de fin sont sur la même ligne cependant
:-p
5
Veuillez noter : si vous utilisez fs.createWriteStream, utilisez flags. Si vous utilisez fs.writeFilealors c'est le cas flag. Veuillez consulter Node JS Docs - File System pour plus d'informations.
Anish Nair
2
Faites attention! Le paramètre n'est pas "drapeaux" mais "drapeau" (singulier): nodejs.org/api/…
Benny Neugebauer
25

En outre appendFile, vous pouvez également passer un indicateur writeFilepour ajouter des données à un fichier existant.

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

En passant le drapeau 'a', les données seront ajoutées à la fin du fichier.

UN J
la source
3
Veuillez noter : si vous utilisez fs.createWriteStream, utilisez flags. Si vous utilisez fs.writeFilealors c'est le cas flag. Veuillez consulter Node JS Docs - File System pour plus d'informations.
Anish Nair
19

Vous devez l'ouvrir, puis y écrire.

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');
    });
  });
});

Voici quelques liens qui vous aideront à expliquer les paramètres

ouvrir
écrire
fermer


EDIT : Cette réponse n'est plus valide, examinez la nouvelle méthode fs.appendFile pour l'ajout.

Corey Hart
la source
1
ressembler à supercobra écrit constamment le journal dans le fichier journal, l'utilisation de fs.write n'est pas recommandée dans ce cas, utilisez plutôt fs.createWriteStream. Lire nodejs.org/docs/v0.4.8/api/all.html#fs.write
angry kiwi
10
La réponse n'est plus exacte à partir de nodejs v0.4.10.
Ruben Tan
il devrait être '0666' plutôt que 666.
Ya Zhuang
13

Node.js 0.8 a fs.appendFile:

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

Documentation

chbrown
la source
3
fd = fs.openSync(path.join(process.cwd(), 'log.txt'), 'a')
fs.writeSync(fd, 'contents to append')
fs.closeSync(fd)
Luis R.
la source
5
quoi que ce soit sync () est presque toujours une mauvaise idée à moins que vous ne soyez sûr à 100% d'en avoir absolument BESOIN. Même alors, vous vous trompez probablement.
Zane Claes
4
Cela ne veut pas dire que c'est faux. Il le fait simplement de manière synchrone. Ce n'est peut-être pas la meilleure pratique pour Node.js, mais c'est pris en charge.
Luis R.
2
J'utilisais "ur doin it evil" dans le sens familier de la phrase Internet-meme. Évidemment, c'est pris en charge = P
Zane Claes
7
Convenu sur async, mais parfois si vous écrivez simplement un script interactif, la synchronisation est très bien.
bryanmac
7
Écrire de manière synchrone est absolument correct si vous utilisez une application en ligne de commande pour un seul utilisateur (par exemple un script pour faire certaines choses). De cette façon, il est plus rapide de faire des choses. Pourquoi le nœud aurait-il des méthodes de synchronisation sinon à cette fin?
Jan Święcki
3

Si vous voulez un moyen simple et sans stress d'écrire les journaux ligne par ligne dans un fichier, alors je recommande 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');

Testé avec Node v8.9.4.

Benny Neugebauer
la source
2

Mon approche est assez particulière. J'utilise essentiellement la WriteStreamsolution mais sans réellement «fermer» le fd en utilisant stream.end(). Au lieu de cela, j'utilise cork/ uncork. Cela a l'avantage de la faible utilisation de la RAM (si cela importe pour tout le monde) et je pense qu'il est plus sûr d'utiliser pour la journalisation / l'enregistrement (mon cas d'utilisation d'origine).

Voici un exemple assez simple. Remarquez que je viens d'ajouter une pseudo forboucle pour la vitrine - dans le code de production, j'attends des messages websocket.

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

uncork va vider les données dans le fichier dans la prochaine coche.

Dans mon scénario, il existe des pics allant jusqu'à ~ 200 écritures par seconde dans différentes tailles. Pendant la nuit, cependant, seules quelques écritures par minute sont nécessaires. Le code fonctionne de manière extrêmement fiable même pendant les périodes de pointe.

Tom-Oliver Heidel
la source
1

Je propose cette suggestion uniquement parce que le contrôle des drapeaux ouverts est parfois utile, par exemple, vous pouvez d'abord le tronquer un fichier existant, puis y ajouter une série d'écritures - dans ce cas, utilisez le drapeau «w» lors de l'ouverture du fichier et ne le fermez pas avant d'avoir terminé toutes les écritures. Bien sûr, appendFile peut être ce que vous recherchez :-)

  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
la source
1

Utilisation du package jfile :

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

Utiliser fs.appendFileou fsPromises.appendFilesont les options les plus rapides et les plus robustes lorsque vous devez ajouter quelque chose à un fichier.

Contrairement à certaines des réponses suggérées, si le chemin du fichier est fourni à la appendFilefonction, il se ferme en fait par lui-même . Ce n'est que lorsque vous passez un descripteur de fichier que vous obtenez quelque chose comme fs.open()vous devez prendre soin de le fermer.

Je l'ai essayé avec plus de 50 000 lignes dans un fichier.

Exemples :

(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.'
  );
})();

entrez la description de l'image ici

Réf: https://github.com/nodejs/node/issues/7560
Exemple d'exécution: https://github.com/apickjs/apickFS/blob/master/writeLines.js

vivek agarwal
la source
0

Voici un script complet. Remplissez vos noms de fichiers et exécutez-le et cela devrait fonctionner! Voici un didacticiel vidéo sur la logique derrière le 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
la source
0

un moyen plus simple de le faire est

const fs = require('fs');
fs.appendFileSync('file.txt', 'message to append into file');
Adeojo Emmanuel IMM
la source
Plus simple que quoi? Avez-vous coché la première réponse, qui donne exactement la même appendFileSyncsolution?
Dan Dascalescu
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
la source
0

En plus de la réponse de denysonique , le type parfois asynchrone de appendFileet d'autres méthodes asynchrones dans NodeJS sont utilisés où la promesse revient au lieu de passer le rappel. Pour ce faire, vous devez envelopper la fonction avec promisifyHOF ou importer des fonctions asynchrones à partir de l'espace de noms des promesses:

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

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

J'espère que ça va aider 😉

Alex Gusev
la source