132 votes

Lire tous les fichiers d'un répertoire, les stocker dans des objets, et envoyer l'objet

Je ne sais pas si c'est possible, mais c'est parti. Et travailler avec des callbacks rend les choses encore plus difficiles.

J'ai un répertoire avec des fichiers html que je veux renvoyer au client dans des morceaux d'objets avec node.js et socket.io.

Tous mes fichiers sont dans /tmpl

Ainsi, socket doit lire tous les fichiers dans /tmpl.

pour chaque fichier, il doit stocker les données dans un objet avec le nom du fichier comme clé, et le contenu comme valeur.

  var data;
  // this is wrong because it has to loop trough all files.
  fs.readFile(__dirname + '/tmpl/filename.html', 'utf8', function(err, html){
      if(err) throw err;
      //filename must be without .html at the end
      data['filename'] = html;
  });
  socket.emit('init', {data: data});

Le rappel final est également faux. Il doit être appelé lorsque tous les fichiers du répertoire sont terminés.

Mais je ne sais pas comment créer le code, quelqu'un sait-il si c'est possible ?

225voto

stewe Points 14623

Donc, il y a trois parties. La lecture, le stockage et l'envoi.

Voici la partie lecture :

var fs = require('fs');

function readFiles(dirname, onFileContent, onError) {
  fs.readdir(dirname, function(err, filenames) {
    if (err) {
      onError(err);
      return;
    }
    filenames.forEach(function(filename) {
      fs.readFile(dirname + filename, 'utf-8', function(err, content) {
        if (err) {
          onError(err);
          return;
        }
        onFileContent(filename, content);
      });
    });
  });
}

Voici la partie stockage :

var data = {};
readFiles('dirname/', function(filename, content) {
  data[filename] = content;
}, function(err) {
  throw err;
});

La partie envoi est à votre charge. Vous pouvez les envoyer un par un ou après les avoir lus.

Si vous voulez envoyer des fichiers après l'achèvement de la lecture, vous devez soit utiliser les versions synchronisées de fs ou utiliser des promesses. Les callbacks asynchrones ne sont pas un bon style.

En outre, vous avez posé une question sur le démontage d'une extension. Vous devez répondre aux questions une par une. Personne n'écrira une solution complète juste pour vous.

32voto

pldg Points 447

Pour tous les exemples ci-dessous, vous devez importer fs y chemin modules :

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

Lire les fichiers de manière asynchrone

function readFiles(dir, processFile) {
  // read directory
  fs.readdir(dir, (error, fileNames) => {
    if (error) throw error;

    fileNames.forEach(filename => {
      // get current file name
      const name = path.parse(filename).name;
      // get current file extension
      const ext = path.parse(filename).ext;
      // get current file path
      const filepath = path.resolve(dir, filename);

      // get information about the file
      fs.stat(filepath, function(error, stat) {
        if (error) throw error;

        // check if the current path is a file or a folder
        const isFile = stat.isFile();

        // exclude folders
        if (isFile) {
          // callback, do something with the file
          processFile(filepath, name, ext, stat);
        }
      });
    });
  });
}

Utilisation :

// use an absolute path to the folder where files are located
readFiles('absolute/path/to/directory/', (filepath, name, ext, stat) => {
  console.log('file path:', filepath);
  console.log('file name:', name);
  console.log('file extension:', ext);
  console.log('file information:', stat);
});

Lecture synchrone des fichiers, stockage dans un tableau, tri naturel

/**
 * @description Read files synchronously from a folder, with natural sorting
 * @param {String} dir Absolute path to directory
 * @returns {Object[]} List of object, each object represent a file
 * structured like so: `{ filepath, name, ext, stat }`
 */
function readFilesSync(dir) {
  const files = [];

  fs.readdirSync(dir).forEach(filename => {
    const name = path.parse(filename).name;
    const ext = path.parse(filename).ext;
    const filepath = path.resolve(dir, filename);
    const stat = fs.statSync(filepath);
    const isFile = stat.isFile();

    if (isFile) files.push({ filepath, name, ext, stat });
  });

  files.sort((a, b) => {
    // natural sort alphanumeric strings
    // https://stackoverflow.com/a/38641281
    return a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' });
  });

  return files;
}

Utilisation :

// return an array list of objects
// each object represent a file
const files = readFilesSync('absolute/path/to/directory/');

Lecture asynchrone des fichiers à l'aide d'une promesse

Plus d'informations sur promisify dans ce artículo .

const { promisify } = require('util');

const readdir_promise = promisify(fs.readdir);
const stat_promise = promisify(fs.stat);

function readFilesAsync(dir) {
  return readdir_promise(dir, { encoding: 'utf8' })
    .then(filenames => {
      const files = getFiles(dir, filenames);

      return Promise.all(files);
    })
    .catch(err => console.error(err));
}

function getFiles(dir, filenames) {
  return filenames.map(filename => {
    const name = path.parse(filename).name;
    const ext = path.parse(filename).ext;
    const filepath = path.resolve(dir, filename);

    return stat({ name, ext, filepath });
  });
}

function stat({ name, ext, filepath }) {
  return stat_promise(filepath)
    .then(stat => {
      const isFile = stat.isFile();

      if (isFile) return { name, ext, filepath, stat };
    })
    .catch(err => console.error(err));
}

Utilisation :

readFiles('absolute/path/to/directory/')
  // return an array list of objects
  // each object is a file
  // with those properties: { name, ext, filepath, stat }
  .then(files => console.log(files))
  .catch(err => console.log(err));

Note : retourner undefined pour les dossiers, si vous voulez vous pouvez les filtrer :

readFiles('absolute/path/to/directory/')
  .then(files => files.filter(file => file !== undefined))
  .catch(err => console.log(err));

21voto

loretoparisi Points 1206

Il s'agit d'un Promise de la version précédente, en utilisant une Promise.all pour résoudre toutes les promesses lorsque tous les fichiers ont été lus :

/**
 * Promise all
 * @author Loreto Parisi (loretoparisi at gmail dot com)
 */
function promiseAllP(items, block) {
    var promises = [];
    items.forEach(function(item,index) {
        promises.push( function(item,i) {
            return new Promise(function(resolve, reject) {
                return block.apply(this,[item,index,resolve,reject]);
            });
        }(item,index))
    });
    return Promise.all(promises);
} //promiseAll

/**
 * read files
 * @param dirname string
 * @return Promise
 * @author Loreto Parisi (loretoparisi at gmail dot com)
 * @see http://stackoverflow.com/questions/10049557/reading-all-files-in-a-directory-store-them-in-objects-and-send-the-object
 */
function readFiles(dirname) {
    return new Promise((resolve, reject) => {
        fs.readdir(dirname, function(err, filenames) {
            if (err) return reject(err);
            promiseAllP(filenames,
            (filename,index,resolve,reject) =>  {
                fs.readFile(path.resolve(dirname, filename), 'utf-8', function(err, content) {
                    if (err) return reject(err);
                    return resolve({filename: filename, contents: content});
                });
            })
            .then(results => {
                return resolve(results);
            })
            .catch(error => {
                return reject(error);
            });
        });
  });
}

Comment l'utiliser :

C'est aussi simple que cela :

readFiles( EMAIL_ROOT + '/' + folder)
.then(files => {
    console.log( "loaded ", files.length );
    files.forEach( (item, index) => {
        console.log( "item",index, "size ", item.contents.length);
    });
})
.catch( error => {
    console.log( error );
});

Supposons que vous ayez une autre liste de dossiers, vous pouvez aussi bien itérer sur cette liste, puisque la promesse interne.all résoudra chacun d'entre eux de manière asynchrone :

var folders=['spam','ham'];
folders.forEach( folder => {
    readFiles( EMAIL_ROOT + '/' + folder)
    .then(files => {
        console.log( "loaded ", files.length );
        files.forEach( (item, index) => {
            console.log( "item",index, "size ", item.contents.length);
        });
    })
    .catch( error => {
        console.log( error );
    });
});

Comment cela fonctionne

El promiseAll fait la magie. Il prend un bloc de fonction de signature function(item,index,resolve,reject) , donde item est l'élément courant du tableau, index sa position dans le tableau, et resolve y reject le site Promise fonctions de rappel. Chaque promesse sera poussée dans un tableau à l'emplacement actuel de la fonction index et avec le courant item comme arguments par le biais d'un appel de fonction anonyme :

promises.push( function(item,i) {
        return new Promise(function(resolve, reject) {
            return block.apply(this,[item,index,resolve,reject]);
        });
    }(item,index))

Alors toutes les promesses seront résolues :

return Promise.all(promises);

5voto

Bimal Grg Points 735

Êtes-vous une personne paresseuse comme moi et aimez module npm :D alors regardez ça.

npm install node-dir

exemple pour la lecture de fichiers :

var dir = require('node-dir');

dir.readFiles(__dirname,
    function(err, content, next) {
        if (err) throw err;
        console.log('content:', content);  // get content of files
        next();
    },
    function(err, files){
        if (err) throw err;
        console.log('finished reading files:', files); // get filepath 
   });

5voto

Sachin Jani Points 88

Je viens d'écrire ça et ça me semble plus propre :

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

const readdir = util.promisify(fs.readdir);
const readFile = util.promisify(fs.readFile);

const readFiles = async dirname => {
    try {
        const filenames = await readdir(dirname);
        console.log({ filenames });
        const files_promise = filenames.map(filename => {
            return readFile(dirname + filename, 'utf-8');
        });
        const response = await Promise.all(files_promise);
        //console.log({ response })
        //return response
        return filenames.reduce((accumlater, filename, currentIndex) => {
            const content = response[currentIndex];
            accumlater[filename] = {
                content,
            };
            return accumlater;
        }, {});
    } catch (error) {
        console.error(error);
    }
};

const main = async () => {

    const response = await readFiles(
        './folder-name',
    );
    console.log({ response });
};

Vous pouvez modifier le response format selon vos besoins. Le site response de ce code sera le suivant :

{
   "filename-01":{
      "content":"This is the sample content of the file"
   },
   "filename-02":{
      "content":"This is the sample content of the file"
   }
}

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