Lesen Sie alle Dateien in einem Verzeichnis, speichern Sie sie in Objekten und senden Sie das Objekt


89

Ich weiß nicht, ob dies möglich ist, aber hier geht es weiter. Und die Arbeit mit Rückrufen macht es noch schwieriger.

Ich habe ein Verzeichnis mit HTML-Dateien, das ich in Object Chunks mit node.js und socket.io an den Client zurücksenden möchte.

Alle meine Dateien sind in / tmpl

Socket muss also alle Dateien in / tmpl lesen.

Für jede Datei müssen die Daten in einem Objekt mit dem Dateinamen als Schlüssel und dem Inhalt als Wert gespeichert werden.

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

Der letzte Rückruf ist ebenfalls falsch. Es muss aufgerufen werden, wenn alle Dateien im Verzeichnis fertig sind.

Aber ich weiß nicht, wie man den Code erstellt, weiß jemand, ob dies möglich ist?


4
Wenn der synchrone Zugriff in Ordnung ist, können Sie den Ereignishandler mithilfe der Methoden (Blockieren) readfileSyncund überspringen readdirSync. nodejs.org/docs/v0.4.8/api/fs.html#fs.readdirSync
rjz

Ok, ich wusste nichts über das Readdir, das kann hilfreich sein. Und was sind die Nachteile des Blockierens? Ich dachte, der springende Punkt von node.js war, dass es nicht blockiert? Warum können wir plötzlich blockieren.
Saif Bechan

Für asynchrone Rückrufe lesen Sie Folgendes : stackoverflow.com/questions/18983138/… Es gibt viele falsche Antworten, aber einige sind richtig. Einer von ihnen benutzt Zähler.
Vanuan

Antworten:


162

Es gibt also drei Teile. Lesen, Speichern und Senden.

Hier ist der Leseteil:

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

Hier ist der Speicherteil:

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

Der sendende Teil liegt bei Ihnen. Möglicherweise möchten Sie sie einzeln oder nach Abschluss des Lesens senden.

Wenn Sie nach dem Lesen Dateien senden möchten, sollten Sie entweder Synchronisierungsversionen von fsFunktionen verwenden oder Versprechen verwenden. Asynchrone Rückrufe sind kein guter Stil.

Außerdem haben Sie nach dem Entfernen einer Erweiterung gefragt. Sie sollten die Fragen einzeln beantworten. Niemand wird eine Komplettlösung nur für Sie schreiben.


Danke, ich denke ich werde das nutzen. Eine Sache, können Sie erklären, was 0===--ctut.
Saif Bechan

1
Sie könnten es als zwei Zeilen schreiben c--und dann ist if (c===0)das das gleiche. Es dekrementiert nur cum 1und prüft, ob es Null erreicht hat
Stewe

Aber es wird immer 0 sein oder nicht? Sie fügen 1 im foreach hinzu und im gleichen foreach entfernen Sie 1, sodass es immer 0 bleibt, oder irre ich mich? Muss der Scheck nicht sein if(c===files.length), so etwas.
Saif Bechan

5
Aufgrund der asynchronen Natur endet die foreach-Schleife höchstwahrscheinlich, bevor der erste readFileHTML-Code zurückgegeben wird. Sie csollte daher sofort auf x(Anzahl der Dateien) steigen und dann dekrementieren, wenn der HTML-
Code

1
Oh, das ist genau dort eine tiefe Logik, nette. Ich habe viel über Knoten zu lernen. Danke für die Hilfe!
Saif Bechan

16

Dies ist eine moderne PromiseVersion der vorherigen Version, bei der Promise.allalle Versprechen gelöst werden, wenn alle Dateien gelesen wurden:

/**
 * 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);
            });
        });
  });
}

Wie man es benutzt:

So einfach wie:

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

Angenommen, Sie haben eine andere Liste von Ordnern, die Sie auch über diese Liste iterieren können, da das interne Versprechen.all jedes dieser dann asynchron auflöst:

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

Wie es funktioniert

Das promiseAllmacht die Magie. Es wird ein Funktionssignaturblock verwendet function(item,index,resolve,reject), in dem itemsich das aktuelle Element im Array, indexseine Position im Array resolveund rejectdie PromiseRückruffunktionen befinden. Jedes Versprechen wird zum aktuellen Zeitpunkt indexund mit dem aktuellen itemArgument als Argument über einen anonymen Funktionsaufruf in ein Array verschoben:

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

Dann werden alle Versprechen gelöst:

return Promise.all(promises);

1
Großartiger Code Loreto, aber warum nicht return block(item,index,resolve,reject);stattdessen verwenden ? return block.apply(this,[item,index,resolve,reject]);Ich denke, das applymacht es schwieriger zu verstehen - gibt es einen Vorteil, den ich nicht kenne?
NULL Zeiger

1
@NULLpointer danke. Einer der Vorteile der Anwendung besteht darin, dass Sie Arrays verwenden können, um Argumente zu übergeben, und Sie können den Kontext übergeben, in dem Variablen wie self.apply definiert sind (someObjContext, [arg1, arg2]). In diesem speziellen Fall brauchen Sie es eigentlich nicht, aber wenn Sie sich in einer Bibliothek befinden, ist dieser Objektkontext möglicherweise etwas anderes ...
loretoparisi

10

Für alle folgenden Beispiele müssen Sie importieren fs und Pfad - Module:

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

Dateien asynchron lesen

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

Verwendung:

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

Dateien synchron lesen, im Array speichern, natürliche Sortierung

/**
 * @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;
}

Verwendung:

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

Lesen Sie Dateien asynchron mit Versprechen

Weitere Informationen zu Promisify finden Sie in diesem Artikel .

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

Verwendung:

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

Hinweis: Geben Sie undefinedfür Ordner zurück, wenn Sie möchten, können Sie sie herausfiltern:

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

5

Sind Sie eine faule Person wie ich und lieben npm Modul : D dann überprüfen Sie dies aus.

npm install node-dir

Beispiel zum Lesen von Dateien:

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

4

Wenn Sie Node.js 8 oder höher haben, können Sie das neue util.promisify verwenden. (Ich markiere als optional die Teile des Codes, die mit der Neuformatierung als Objekt zu tun haben, die der ursprüngliche Beitrag angefordert hat.)

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

  let files; // optional
  promisify(fs.readdir)(directory).then((filenames) => {
    files = filenames; // optional
    return Promise.all(filenames.map((filename) => {
      return promisify(fs.readFile)(directory + filename, {encoding: 'utf8'});
    }));
  }).then((strArr) => {
    // optional:
    const data = {};
    strArr.forEach((str, i) => {
      data[files[i]] = str;
    });
    // send data here
  }).catch((err) => {
    console.log(err);
  });

3

Eine andere Version mit der modernen Methode von Promise. Es ist kürzer als die anderen Antworten, die auf Promise basieren:

const readFiles = (dirname) => {

  const readDirPr = new Promise( (resolve, reject) => {
    fs.readdir(dirname, 
      (err, filenames) => (err) ? reject(err) : resolve(filenames))
  });

  return readDirPr.then( filenames => Promise.all(filenames.map((filename) => {
      return new Promise ( (resolve, reject) => {
        fs.readFile(dirname + filename, 'utf-8',
          (err, content) => (err) ? reject(err) : resolve(content));
      })
    })).catch( error => Promise.reject(error)))
};

readFiles(sourceFolder)
  .then( allContents => {

    // handle success treatment

  }, error => console.log(error));

schön und knackig! Danke @Paul
Chaos Legion

1

Damit der Code in verschiedenen Umgebungen reibungslos funktioniert, kann path.resolve an Stellen verwendet werden, an denen der Pfad manipuliert wird. Hier ist Code, der besser funktioniert.

Leseteil:

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(path.resolve(dirname, filename), 'utf-8', function(err, content) {
        if (err) {
          onError(err);
          return;
        }
        onFileContent(filename, content);
      });
    });
  });
}

Speicherteil:

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

Wie Sie bemerkt haben Sie noch nicht verdient genügend Ruf zu kommentieren jedem Post . Bitte missbrauchen Sie den Post-Antwort- Button aus diesem Grund nicht. Verbringen Sie lieber einige Zeit auf der Website und stellen Sie Fragen oder geben Sie Antworten. Dadurch erhalten Sie den Repräsentanten, den Sie benötigen.
Scott Weldon

1
@ ScottWeldon akzeptierte Antwort hat Problem. Ich bin mir nicht sicher, ob Ihr Standpunkt gültig ist. Ich werde diese Antwort behalten, sie kann anderen helfen. Versteh mich nicht falsch, aber ich brauche weder den Ruf noch die Punkte. :)
rsa

1

Ich habe das gerade geschrieben und es sieht für mich sauberer aus:

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

Sie können das responseFormat nach Ihren Wünschen ändern . Das responseFormat dieses Codes sieht folgendermaßen aus:

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

0

async / warten

const { promisify } = require("util")
const directory = path.join(__dirname, "/tmpl")
const pathnames = promisify(fs.readdir)(directory)

try {
  async function emitData(directory) {
    let filenames = await pathnames
    var ob = {}
    const data = filenames.map(async function(filename, i) {
      if (filename.includes(".")) {
        var storedFile = promisify(fs.readFile)(directory + `\\${filename}`, {
          encoding: "utf8",
        })
        ob[filename.replace(".js", "")] = await storedFile
        socket.emit("init", { data: ob })
      }
      return ob
    })
  }

  emitData(directory)
} catch (err) {
  console.log(err)
}

Wer möchte es mit Generatoren versuchen?

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.