NodeJS Verzeichnisse mit readdirSync synchron einlesen

NodeJS Verzeichnisse mit readdirsync synchron einlesen
NodeJS Verzeichnisse mit readdirsync synchron einlesen

In NodeJS Verzeichnisse mit readdirSync einlesen zu wollen ist wohl eine der Standard-Aufgaben schlechthin.

Egal ob man zum Beispiel Videos verwalten möchte und Diese dann natürlich aus dem Verzeichnis auflisten muss.

Oder ob man z. B. seine favorisierten Lieder auflisten möchte, damit das Passende dann abgespielt werden kann.

Man kommt in vielen Fällen nicht um die Funktion (oder zumindest analoge Funktionen) „readdirsync“ rum.

Namespace / Modul

Natürlich wird auch hier – wie so oft in der Entwicklung – bestehende Funktionalität verwendet.

Wer möchte schon Funktionen bauen, Welche es schon gibt, also das Rad praktisch neu erfinden.

NodeJS stellt uns daher gewisse Funktionen aus verschiedenen sogenannten Modulen bereit, Welche wir einfach importieren und verwenden können.

Das hierzu benötigte Modul ist das sehr bekannte und in der NodeJS-Welt gängige „fs„-Modul.

Es steht für FileSystem“ und erlaubt es uns als Entwickler – wie der Name schon verraten lässt – auf das Dateisystem zuzugreifen.

Darunter fällt natürlich neben dem Einlesen von Verzeichnissen auch z. B. solche Sachen wie das Verschieben, oder Löschen von Dateien.

Beispiel-Anwendungsfall

NodeJS Verzeichnisse mit readdirsync auslesen - Video-Mediathek
NodeJS Verzeichnisse mit readdirsync auslesen – Video-Mediathek

In unserem kleinen Beispiel werden wir auf die oben schonmal angesprochene Thematik einer Medienverwaltung“ eingehen.

Ich denke, dass dies deshalb gut passt, da es praxisnah und relativ einfach umzusetzen ist.

Dabei werden wir in unserem Projekt einen Ordner namens videos“ haben, Welcher dann die jeweiligen Videos beinhaltet.

Bei Ausführung des Skripts, werden wir die Inhalte – also die Videos – auflisten und in der Konsole ausgeben.

Ich werde der Einfachheit halber zumindest vorerst auf eine Art (Rest-) API verzichten, damit wir uns auf das Modul konzentrieren.

Eventuell ergänze ich dies zu einem späteren Zeitpunkt und erkläre es dann nochmal in einem separaten Abschnitt.

Code – NodeJS Verzeichnisse mit readdirSync auslesen

Zur Erstellung des Codes widmen wir uns zuerst der Anlage eines neuen Projektordners namens z. B. „videomediathek“.

Gehe dazu einfach in Deinen üblichen „Projekte Ordner“ und erstelle dort einfach einen Weiteren.

Anschließend öffnest Du den Code-Editor Deiner Wahl und darin den erstellten Ordner – bei mir ist das Visual Studio Code.

Achtung – Code-Editor & NodeJS

Stelle sicher, dass Du vorher natürlich NodeJS installiert hast, sonst wird das Folgende nicht funktionieren.

Öffne nach der erfolgreichen NodeJS-Installation ein Terminal in Deinem Visual Studio Code.

Das brauchen wir gleich zum Ausführen des noch zu erstellenden Codes.

Visual Studio Code neues Terminal öffnen
Visual Studio Code neues Terminal öffnen

Gehe dafür einfach oben im „Visual Studio Code“-Menü auf Terminal“ und öffne ein neues Terminal.

Alternativ kannst Du natürlich auch den passenden Hotkey „Strg+Shift+ö“ verwenden.

Falls Dein Editor (traurigerweise) kein integriertes Terminal hat, solltest Du „Ihn“ vielleicht direkt entsorgen – Spaß beiseite.

Verwenden dann einfach unter Windows eine normale Konsole (Windows-Taste und „cmd“ eingeben, Enter), oder unter MacOS ein Terminal-Fenster.

Das Skript erstellen

Im nächsten Schritt erstellen wir das notwendige Einstiegs-Skript, also den Einstiegspunkt unserer kleinen App.

Dafür legst Du im Root-Verzeichnis Deines Projekts einfach eine neue Datei namens z. B. „index.js“ an.

Grundsätzlich können wir nun die Skripts, bzw. das Skript ausführen, indem wir im Terminal „node <dateiname>“ also „node index.js“ eingeben und mit Enter bestätigen.

Es sollte auch kürzer, also ohne „.js“ Suffix funktionieren, da „.js“ praktisch die Standard-Dateiendung ist.

Das Modul importieren

Mit dem folgenden Code importieren wir das Modul in eine Konstante und machen es somit für unser Skript verfügbar:

const fs = require('fs')

Da wir den Parameter der „require“-Funktion ohne z. B. einen relativen Pfad aufrufen, schaut Node praktisch in die Standard-Auflistung der verfügbaren Module.

Der Rückgabewert der Funktion gibt uns das „importierteModul zurück und wir speichern dann einen Verweis in die „fs“-Konstante.

Eine var„-Variable, o. Ä. zu verwenden macht hier für mich überhaupt keinen Sinn, leider verwenden auch heute noch viele Tutorials diese Variante.

Schaut man sich nun durch eingeben von „fs.“ und dem analogen Verb read“ einmal die Vorschläge an, kommen wir auch schon an unsere Wunsch-Funktion „readdirSync“:

NodeJS read Funktionen des fs-Moduls
NodeJS read Funktionen des fs-Moduls

Man beachte hier die völlig bescheuerte Schreibweise der Funktion..

NodeJS Verzeichnisse mit readdirSync auslesen

Nun können wir die „readdirSync„-Funktion mit den passenden Parametern aufrufen und dessen Ergebnis in eine Variable zwischenspeichern.

Da ich auch hier nicht vor habe, die Variable anschließend noch einmal zu ändern, verwende ich auch hier das „const“-Schlüsselwort.

Als Pfad (relativ) liefern wir praktisch das aktuelle Verzeichnis durch Übergabe von „./“.

const dir = fs.readdirSync('./')

Geben wir uns nun das Ergebnis im nächsten Schritt durch z. B. ein „Console.log“-Befehl aus, sehen wir folgende Ausgabe:

console.log(dir)
NodeJS console log Ausgabe von readdirSync
NodeJS console log Ausgabe von readdirSync

Das Programm wird durch die Node-Umgebung gestartet, es führt unsere Anweisungen aus und wir erhalten die Ausgabe der im Ordner befindlichen Dateien.

In diesem Fall befindet sich bisher natürlich nur die vorhin von uns erstellte index.js„-Datei in dem ausgegebenen Array.

Bedenke

Bedenke beim Verwenden der synchronen Variante allerdings immer die Auswirkungen, bzw. die potentiellen Folgen.

Durch die synchrone, lineare Ausführung des Codes ist hier nicht wirklich viel im Bereich Asynchronität, bzw. Parallelismus los.

Die Befehle werden nacheinander ausgeführt und müssen auf die Ausführung warten.

Dies schlägt natürlich besonders dann nieder, wenn man einen nach Möglichkeit performanten Server bauen möchte.

Dazu aber in einem späteren Beitrag mehr..

Alternativen – NodeJS Verzeichnisse mit readdirSync auslesen

Alternativ zur nun demonstrierten synchronen Variante gibt es von Node natürlich auch eine meist zu bevorzugende asynchrone Alternative.

Da dies mehr oder weniger der Standard ist – also asynchron zu verfahren – können wir einfach das „Sync“-Suffix streichen und erhalten „readdir„.

Hierbei müssen wir uns allerdings nun an die JavaScript-Gegebenheiten von sogenannten Callbacks halten.

Anders als beim obigen Code wird hier nämlich nicht mehr wartend linear gearbeitet, sondern linear, jedoch mit einer sofortigen Ausführung.

Stell‘ es Dir am besten wie auf dem folgenden Bild mit Zündschnuren vor (ich entschuldige mich jetzt schon für meine Paint-Skills..):

NodeJS asynchrone Befehls-Verarbeitung - fire and forget
NodeJS asynchrone Befehls-Verarbeitung – fire and forget

Links wird die Zündschnur gezündet und der Brand verläuft in ziemlich schneller Geschwindigkeit von links nach rechts.

Dabei spielt es keine Rolle, ob Zündschnur 1 schon abgebrannt ist, oder eben nicht, die Zündung wird in einem Stück nach rechts durchlaufen.

Sie zündet dabei bei Berührung alle roten Punkte an, dessen Zündschnuren dann unabhängig voneinander loslaufen werden.

Im Code passiert dies dann natürlich von oben nach unten gesehen..

Alternativ-Lösung 1

Damit wir nun also folgenden asynchronen Code (also der Prozess passiert im Hintergrund asynchron) aufeinander wartend ausführen lassen können:

const dirResultOne = fs.readdirSync('./')
const dirResultTwo = fs.readdirSync('./')

… können wir einfach die „neuere async/await„-Methodik von JavaScript verwenden.

Dazu müssen wir ein anderes Modul importieren, Welches laut Recherche seit Node 8 verfügbar ist.

Danach verwenden wir das „util„-Modul, um mit Hilfe dessen „promisify“-Funktion die „readdir“-Methode in eine „awaitable“, also erwartbare Funktion „umzuwandeln“.

const util = require('util')
const readdirAsync = util.promisify(fs.readdir)
const dirResultOne = await readdirAsync('./')
const dirResultTwo = await readdirAsync('./')

Durch die „await„-Schlüsselwörter wird nun auf das „Returning“ der Promises gewartet.

Damit das Ganze allerdings klappt, müsste man die Anweisungen in eine Funktion mit einem „async„-Modifizierer packen:

(async function() {
  const dirResultOne = await readdirAsync('./')
  const dirResultTwo = await readdirAsync('./')
  // do something
})()

Man beachte auch die „()“ zum Schluss, Welche dadurch zu einer „Self-Executing-Function“ wird.

Die Funktion wird also definiert und im Anschluss sofort aufgerufen..

Alternativ-Lösung 2

Die nächste alternative Lösung würde man mit Hilfe von Callbacks als Übergabe-Parameter realisieren.

Das sieht dann im Endeffekt so aus:

fs.readdir('./', function (err, dirResultOne) {
  if (err) {
    // handle error..
    return
  }
  console.log(dirResultOne)
  fs.readdir('./', function (errTwo, dirResultTwo) {
    if (errTwo) {
      // handle second error..
      return
    }
    console.log(dirResultTwo)
  })
})

Ich habe dieses zweite Beispiel aber beabsichtigt an den Schluss gepackt, da dies eigentlich nicht mehr wirklich zeitgemäß ist.

Und das aus gutem Grund.. unter anderem wegen der sogenannten „Pyramid of Doom„.

Wenn ich dies nun allerdings hier auch noch abarbeiten würde, würden wir völlig den Rahmen sprengen..

Die „Videomediathek“-API – NodeJS Verzeichnisse mit readdirSync auslesen

Kommen wir nun zum finalen Teil vor der Zusammenfassung: Die Umsetzung der „Videomediathek“-API.

Dazu brauchen wir einen Webserver, den wir entweder mit dem installierbaren Express-Modul realisieren können, oder in Eigenarbeit rangehen.

Da es meiner Meinung nach für dieses kleine Beispiel völliger Overhead wäre, Express zu verwenden, bauen wir uns den kleinen Server mit dem hauseigenen „http“-Modul selbst.

Http-Server aufsetzen

Um den kleinen Http-Server vorzubereiten, hole Dir zuerst eine Referenz auf das Http-Modul:

const http = require('http')

Anschließend definieren wir einen kleinen Request-Handler, also eine Funktion, Welche eine jeweilige Anfrage an den Server beantwortet.

Diese sollte natürlich einerseits als „async„-markiert sein und andererseits JSON-Daten an den Client senden können.

Dazu schreiben wir in der ersten Zeile gewisse Daten in den Kopf der Antwort, also den „Content-Type“-Header.

Danach lesen wir die Inhalte des „videos“-Verzeichnis aus und schreiben Diese per „JSON.stringify“- und „res.write“-Funktion in den Antwort-Stream.

const requestHandler = async function (req, res) {
  res.writeHead(200, { 'Content-Type': 'application/json' })
  const dir = await readDirAsync('./videos')
  res.write(JSON.stringify(dir))
  res.end()
}

Als nächstes speichern wir ein paar gängige Einstellungen wie den „zuzuhörenden“ Port des Servers sowie dessen Host ein.

Zum Schluss starten wir dann den letztendlichen Server:

const port = 8000
const host = 'localhost'
const server = http.createServer(requestHandler)
server.listen(port, host, function () {
  console.log(`listening for video library requests on https://${host}:${port}`)
})

Vergiss nicht, das Ganze dann per „node index.js“ + Enter, innerhalb der Editor-Konsole/Terminal zu starten.

Dieses Mal ist der Server allerdings wie man es von Servern kennt permanent am Laufen und nicht nur „zack fertig“.

Ruft man dann die passende Adresse (https://localhost:8000) in seinem Browser des Vertrauens auf, bekommt man folgende Ansicht:

(Achtung: ich verwende um Platz zu sparen Textdateien..).

NodeJS Verzeichnisse mit readdirsync auslesen - Video-Mediathek Ausgabe
NodeJS Verzeichnisse mit readdirsync auslesen – Video-Mediathek Ausgabe

Diesen in JSON dargestellten Antwort-Standard kann man dann problemlos in jeder gängigen Programmiersprache verarbeiten.

Beenden kannst Du das Ganze übrigens wieder durch zwei-maliges Drücken von „Strg+C“ in der Konsole/Terminal.

Kompletter Code – NodeJS Verzeichnisse mit readdirSync auslesen

const fs = require('fs')
const dir = fs.readdirSync('./')
console.log(dir)

// iterate through elements
// for (let d of dir) {
//   console.log(`Entry: ${d}`);
// }

// use callback alternative
// fs.readdir('./', function (err, dirResultOne) {
//   if (err) {
//     // handle error..
//     return
//   }
//   console.log(dirResultOne)
//   fs.readdir('./', function (errTwo, dirResultTwo) {
//     if (errTwo) {
//       // handle second error..
//       return
//     }
//     console.log(dirResultTwo)
//   })
// })

// const util = require('util')
// const readDirAsync = util.promisify(fs.readdir)
// using self executing function with async/await
// (async function () {
//   const dirResultOne = await readDirAsync('./')
//   const dirResultTwo = await readDirAsync('./')
//   // ...
// })()

// const util = require('util')
// const readDirAsync = util.promisify(fs.readdir)
// const http = require('http')
// const requestHandler = async function (req, res) {
//   res.writeHead(200, { 'Content-Type': 'application/json' })
//   const dir = await readDirAsync('./videos')
//   res.write(JSON.stringify(dir))
//   res.end()
// }
// const port = 8000
// const host = 'localhost'
// const server = http.createServer(requestHandler)
// server.listen(port, host, function () {
//   console.log(`listening for video library requests on https://${host}:${port}`)
// })

Downloads

Weiterführende Links

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.