114 votes

Créer un fichier uniquement s'il n'existe pas dans Node.js

Nous avons un tampon que nous aimerions écrire dans un fichier. Si le fichier existe déjà, nous devons incrémenter un index sur celui-ci et réessayer. Existe-t-il un moyen de créer un fichier uniquement s'il n'existe pas, ou dois-je simplement statuer sur les fichiers jusqu'à ce que j'obtienne une erreur pour en trouver un qui n'existe pas déjà ?

Par exemple, j'ai des fichiers a_1.jpg y a_2.jpg . J'aimerais que ma méthode essaie de créer a_1.jpg y a_2.jpg et échouer, et enfin créer avec succès a_3.jpg .

La méthode idéale ressemblerait à ceci :

fs.writeFile(path, data, { overwrite: false }, function (err) {
  if (err) throw err;
  console.log('It\'s saved!');
});

ou comme ça :

fs.createWriteStream(path, { overwrite: false });

Est-ce que quelque chose de ce genre existe dans les fs bibliothèque ?

EDIT : Ma question n'est pas de savoir s'il existe une fonction distincte qui vérifie l'existence. C'est la suivante : existe-t-il un moyen de créer un fichier s'il n'existe pas, en un seul appel au système de fichiers ?

99voto

polkovnikov.ph Points 1041

Comme votre intuition l'a correctement deviné, la solution naïve avec une paire de exists / writeFile Les appels sont erronés. Le code asynchrone fonctionne de manière imprévisible. Et dans ce cas précis, c'est

  • Y a-t-il un fichier a.txt ? - Non.
  • (Dossier a.txt est créé par un autre programme)
  • Écrire à a.txt si c'est possible. - D'accord.

Mais oui, nous pouvons le faire en un seul appel. Nous travaillons avec un système de fichiers, donc c'est une bonne idée de lire le manuel du développeur sur fs . Et hé, voici une partie intéressante.

w' - Ouvre le fichier pour l'écriture. Le fichier est créé (s'il n'existe pas) existe pas) ou tronqué (s'il existe).

wx' - Comme 'w' mais échoue si le chemin existe.

Donc tout ce que nous avons à faire est d'ajouter wx a la fs.open appel. Mais hé, nous n'aimons pas fopen -comme IO. Lisez la suite fs.writeFile un peu plus.

fs.readFile(nom du fichier[, options], callback)#.

filename Chaîne

Options Objet

encodage Chaîne | Null par défaut = null

flag String default = 'r

Fonction callback

Ce options.flag semble prometteur. Nous essayons donc

fs.writeFile(path, data, { flag: 'wx' }, function (err) {
    if (err) throw err;
    console.log("It's saved!");
});

Et cela fonctionne parfaitement pour une seule écriture. Je pense que ce code échouera de manière encore plus bizarre si vous essayez de résoudre votre tâche avec lui. Vous avez un atomaire "check for a_#.jpg existence, et y écrire s'il est vide", mais toutes les autres opérations de l'opération fs n'est pas verrouillé, et a_1.jpg peut disparaître spontanément alors que vous êtes déjà en train de vérifier. a_5.jpg . La plupart * Les systèmes de fichiers ne sont pas ACID et le fait que vous soyez capable d'effectuer au moins quelques opérations atomiques est miraculeux. Il est très probable que wx Le code ne fonctionnera pas sur certaines plateformes. Donc pour le bien de votre santé mentale, utiliser la base de données, enfin .

Quelques informations supplémentaires pour ceux qui souffrent

Imaginez que nous écrivions quelque chose comme memoize-fs qui met en cache les résultats des appels de fonctions dans le système de fichiers pour nous faire gagner du temps réseau/cpu. Pourrions-nous ouvrir le fichier en lecture s'il existe, et en écriture s'il n'existe pas, tout cela en un seul appel ? Jetons un regard amusé sur ces drapeaux. Après un certain nombre d'exercices mentaux, nous pouvons voir que a+ fait ce que nous voulons : si le fichier n'existe pas, il en crée un et l'ouvre à la fois pour la lecture et l'écriture, et si le fichier existe, il le fait sans effacer le fichier (comme le fait w+ serait). Mais maintenant, nous ne pouvons l'utiliser ni dans (smth)File ni dans create(Smth)Stream fonctions. Et cela semble être une fonction manquante.

N'hésitez donc pas à déposer une demande de fonctionnalité (ou même un bogue) sur Node.js github, car l'absence d'API de système de fichiers asynchrones atomiques est un inconvénient de Node. Bien que n'attendez pas ne changera pas de sitôt.

Éditer. Je souhaite créer un lien vers les articles de Linus et par Dan Luu sur pourquoi exactement vous ne voulez pas faire quelque chose d'intelligent avec votre fs des appels, parce que la revendication a été laissée en grande partie non fondée sur quoi que ce soit.

39voto

Alvaro Points 9684

Et si vous utilisiez le a option ?

Selon les docs :

'a+' - Ouvrir le fichier pour la lecture et l'ajout. Le fichier est créé s'il n'existe pas.

Il semble fonctionner parfaitement avec createWriteStream

12voto

Kayvar Points 4239

Cette méthode n'est plus recommandée. fs.exists est déprécié. Voir les commentaires.

Voici quelques options :

1) Avoir 2 appels "fs . Le premier est le " fs.existe "et le second est un appel " fs.write / lire, etc"

//checks if the file exists. 
//If it does, it just calls back.
//If it doesn't, then the file is created.
function checkForFile(fileName,callback)
{
    fs.exists(fileName, function (exists) {
        if(exists)
        {
            callback();
        }else
        {
            fs.writeFile(fileName, {flag: 'wx'}, function (err, data) 
            { 
                callback();
            })
        }
    });
}

function writeToFile()
{
    checkForFile("file.dat",function()
    {
       //It is now safe to write/read to file.dat
       fs.readFile("file.dat", function (err,data) 
       {
          //do stuff
       });
    });
}

2) Ou Créer un fichier vide d'abord :

--- Synchronisation :

//If you want to force the file to be empty then you want to use the 'w' flag:

var fd = fs.openSync(filepath, 'w');

//That will truncate the file if it exists and create it if it doesn't.

//Wrap it in an fs.closeSync call if you don't need the file descriptor it returns.

fs.closeSync(fs.openSync(filepath, 'w'));

--- ASync :

var fs = require("fs");
fs.open(path, "wx", function (err, fd) {
    // handle error
    fs.close(fd, function (err) {
        // handle error
    });
});

3) Ou utiliser " touchez " : https://github.com/isaacs/node-touch

7voto

Kumar Vaibhav Points 604

Pour faire cela en un seul appel système, vous pouvez utiliser la fonction fs-extra module npm. Après cela, le fichier aura été créé ainsi que le répertoire dans lequel il doit être placé.

const fs = require('fs-extra');
const file = '/tmp/this/path/does/not/exist/file.txt'
fs.ensureFile(file, err => {
    console.log(err) // => null
});

Une autre façon est d'utiliser ensureFileSync qui fera la même chose mais de manière synchrone.

const fs = require('fs-extra');
const file = '/tmp/this/path/does/not/exist/file.txt'
fs.ensureFileSync(file)

3voto

flolu Points 837

Con async / await et Typescript je ferais :

import * as fs from 'fs'

async function upsertFile(name: string) {
  try {
    // try to read file
    await fs.promises.readFile(name)
  } catch (error) {
    // create empty file, because it wasn't found
    await fs.promises.writeFile(name, '')
  }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X