‫-: Okay, jetzt können wir tatsächlich mit Streams arbeiten.
‫Und wieder, beginnend mit dem Erstellen einer neuen Datei.
‫Gut. Nehmen wir nun an,
‫dass wir aus irgendeinem Grund in unserer Anwendung eine große Textdatei aus
‫dem Dateisystem lesen und dann an den Client senden müssen.
‫Also, wie würden wir das machen?
‫Nun, es gibt mehrere Möglichkeiten, und wir werden
‫einige davon untersuchen, beginnend mit der
‫einfachsten bis hin zur besten Methode, dies zu tun.
‫Okay, als erstes müssen Sie wie
‫zuvor das Dateisystempaket und auch das HTTP-Modul benötigen.
‫Lassen Sie mich Ihnen jetzt einen netten Trick zeigen, also
‫lassen Sie uns einen Server wie diesen erstellen.
‫Benötigen Sie also das HTTP-Paket, und von hier aus können
‫wir sofort createServer aufrufen. Also einfach so.
‫Okay, das Ergebnis der Anforderung von HTTP ist das
‫HTTP-Objekt und dort können wir dann createServer verwenden.
‫Einfach so und dann in einer Variablen speichern.
‫Das ist also eine weitere Möglichkeit, einen neuen Server
‫mit noch etwas weniger Code zu erstellen.
‫Okay und so, wie zuvor, listen wir jetzt das
‫Anfrageereignis auf und geben unseren Rückruf hier an.
‫Die erste Lösung, die wir verwenden werden,
‫ist also die einfachste und unkomplizierteste.
‫Das heißt, Sie lesen die Datei einfach in eine
‫Variable ein und senden sie dann an
‫den Client, so wie wir es bereits kennen.
‫Das ist also ganz einfach, und lassen Sie mich
‫dazu hier einen Kommentar schreiben. Also Lösung
‫eins und so fs. readFile dann wieder unsere Testdatei.
‫Und wenn das fertig ist, rufen wir unsere Callback-Funktion auf, in
‫der wir Zugriff auf die Daten haben.
‫Aber zuerst behandeln wir den Fehler.
‫Also zum Beispiel für den Fall, dass die Datei nicht existiert, und in
‫diesem Fall protokollieren wir den Fehler einfach in der Konsole.
‫Aber ansonsten senden wir diese Daten einfach an den Kunden zurück.
‫Also verwenden wir das Response-Objekt, wie wir es schon
‫oft getan haben, und dann . Ende und die Daten.
‫Also, speichern Sie es, und das ist die
‫erste und einfachste Lösung, oder?
‫Aber bevor wir das testen können, müssen wir eigentlich auch
‫noch einen Server starten, oder? Also verwenden wir zuhören, um das zu tun.
‫Und der Port, der gleiche wie zuvor, dann localhost
‫und unsere Callback-Funktion.
‫Okay, mal sehen, ob das wirklich funktioniert.
‫Und natürlich nicht, denn eigentlich habe ich die Bewerbung gar
‫nicht gestartet.
‫Jetzt heißt es also zuhören, und jetzt sehen wir,
‫was hier passiert.
‫Und es geht los. Diese Datei ist also riesig,
‫sie hat Node. js ist am besten darin
‫geschrieben, etwa 10000 Mal oder so, und daher dauert es
‫lange, bis es vollständig geladen ist. Und wir sind nicht wirklich daran
‫interessiert, alles zu laden, also stoppen wir hier und kehren Sie
‫zu unserem Code zurück. Das funktioniert also gut, die
‫Lösung, die wir hier in diesem Fall haben, aber das
‫Problem ist, dass der Knoten bei dieser Lösung
‫tatsächlich die gesamte Datei in den Speicher laden muss,
‫da er erst dann die Daten senden kann, wenn
‫er fertig ist . Dies ist ein Problem,
‫wenn die Datei groß ist und wenn viele Anfragen
‫Ihren Server erreichen. Da dem Knotenprozess
‫sehr schnell die Ressourcen ausgehen und Ihre App nicht mehr
‫funktioniert, stürzt alles ab und Ihre Benutzer werden nicht glücklich
‫sein, glauben Sie mir. Diese Lösung hier
‫funktioniert also, wenn wir beispielsweise nur für
‫uns selbst etwas Kleines lokal erstellen.
‫Aber in einer produktionsreifen Anwendung können Sie einen solchen
‫Code nicht verwenden. Okay? Kommen wir also zu
‫unserer zweiten Lösung. Und in
‫dieser Lösung werden wir tatsächlich Streams verwenden.
‫Okay, kommentieren wir diesen Teil und gehen
‫Sie zu Lösung Nummer zwei.
‫Und die Idee dabei ist, dass wir
‫diese Daten eigentlich nicht wirklich aus der Datei in eine Variable einlesen müssen, oder?
‫Wir brauchen diese Variable nicht. Anstatt also die
‫Daten in eine Variable einzulesen und diese Variable im
‫Speicher zu speichern, erstellen wir einfach einen lesbaren Stream.
‫Wenn wir dann jeden Datenblock erhalten, senden wir ihn
‫als Antwort an den Client, bei
‫der es sich um einen beschreibbaren Stream handelt.
‫Lassen Sie mich Ihnen nun zeigen, wie wir Streams verwenden können.
‫Also erstellen wir eine Variable, nennen wir sie hier lesbar,
‫und dann wieder aus dem Dateisystem, verwenden wir
‫createReadStream Dann das natürlich klein. Und jetzt der Name der Datei, die
‫wir zu lesen versuchen. Das ist also wieder Testdatei. TXT.
‫Okay, perfekt. Dadurch wird nun aus den Daten, die
‫sich in dieser Textdatei befinden, ein Stream erstellt, den wir dann
‫Stück für Stück konsumieren können. Also Stück für Stück.
‫Und wie machen wir das? Nun, erinnern Sie
‫sich aus der letzten Vorlesung, dass jedes Mal, wenn
‫wir ein neues Stück Daten konsumieren können, ein lesbarer
‫Stream das Datenereignis ausgibt. Okay, und damit können wir uns
‫das anhören, so wie wir es in der Veranstaltungsvorlesung gelernt haben.
‫Also lesbar. on, data und dann unsere Callback-Funktion.
‫Und in unserer Callback-Funktion haben wir Zugriff auf diese
‫Daten, also auf diesen Chunk.
‫Lassen Sie es mich hier in unserer
‫Callback-Funktion Chunk nennen, damit wir jetzt mit diesem Datenstück umgehen können.
‫Und was wir mit diesem
‫Datenstück, mit diesem Chunk, tun werden, ist, es tatsächlich
‫in einen beschreibbaren Stream zu schreiben, was die Antwort ist.
‫Also, res. schreiben, dieser Brocken. Okay?
‫Denken Sie also noch einmal daran, dass diese
‫Antwort ein beschreibbarer Stream ist. Also genau wie ich
‫im vorherigen Video erwähnt habe, oder? Und so können wir jetzt
‫die Methode write verwenden, um jedes einzelne Datenelement in diesen Stream zu senden.
‫Okay, und damit streamen wir effektiv
‫den Inhalt aus der Datei direkt an den Client.
‫Okay, verstehst du den Unterschied?
‫Zuvor schreiben wir also alles auf einmal in eine Variable, und
‫sobald diese fertig war, haben wir die gesamten Daten
‫an den Client zurückgesendet. Aber in dieser
‫Situation mit dem Stream ist das anders.
‫Wir streamen die Datei effektiv, also lesen wir
‫einen Teil der Datei, und sobald dieser
‫verfügbar ist, senden wir ihn mit der Schreibmethode des Antwortstreams direkt
‫an den Client. Wenn dann das nächste
‫Stück verfügbar ist, wird dieses Stück gesendet, und zwar so
‫lange, bis die gesamte Datei gelesen und an den Client gestreamt wurde.
‫Okay, zum Schluss müssen wir noch das Ereignis
‫verarbeiten, wenn alle Daten gelesen wurden.
‫Also, wenn der Stream im Grunde fertig ist, die Daten aus der
‫Datei zu lesen.
‫In diesem Fall wird also das
‫Endereignis ausgegeben, und sobald das passiert, werden wir
‫res tun. Ende, okay? Und wir haben diesen hier
‫schon einmal verwendet, also haben wir bei der Antwort Ende aufgerufen, das haben wir schon einmal gemacht, oder?
‫Und jetzt macht es tatsächlich mehr Sinn, denn die
‫Antwort ist auch wieder ein Stream, und die Endmethode
‫signalisiert, dass keine Daten mehr in diesen beschreibbaren
‫Stream geschrieben werden, okay?
‫Also hier oben haben wir nur res verwendet. mit den darin enthaltenen
‫Daten enden. Wir haben also kein Streaming
‫gemacht, sondern am Ende nur ein paar Daten gesendet.
‫In diesem Fall übergeben wir
‫nichts an diese end-Methode, da wir bereits alle Daten mit
‫res gesendet haben. write, Stück für
‫Stück, und wenn der lesbare Stream damit
‫fertig ist, seine Datei zu lesen, müssen wir nur noch signalisieren,
‫dass wir mit res bereit sind. Ende so, okay?
‫Wir müssen diese Daten und dieses Endereignis also immer hier
‫nacheinander so verwenden. Denn sonst
‫wird die Antwort eigentlich nie wirklich
‫an den Client gesendet. Okay, also ohne dieses Stück
‫hier würde diese gesamte Lösung nicht wirklich funktionieren, okay?
‫Also lass uns jetzt dieses schließen, neu beginnen
‫und noch einmal lesen, und damit du siehst, dass es
‫tatsächlich wieder funktioniert, okay? Jetzt diesmal ohne die Probleme, die wir
‫mit der ersten Lösung hatten. Lassen Sie uns dies hier beenden
‫und zu unserem Code zurückkehren, denn ich möchte
‫Ihnen jetzt zeigen, dass es ein weiteres Ereignis gibt,
‫das wir in einem lesbaren Stream anhören können, nämlich das Fehlerereignis.
‫Also lesbar. on('error') und in dieser Callback-Funktion
‫haben wir Zugriff auf das Fehlerobjekt.
‫Okay, in diesem Fall werden wir diesen Fehler in der Konsole
‫protokollieren und dann das Ergebnis der Datei nicht gefunden senden.
‫Und wir können den Statuscode auch auf einen Fehler
‫setzen, also zum Beispiel 500. Normalerweise wird es automatisch auf
‫200 gesetzt, was in Ordnung bedeutet, aber in diesem Fall haben wir einen
‫Serverfehler, was bedeutet, dass wir den Code 500 zurücksenden möchten.
‫Alles klar, also lass uns jetzt...
‫Eigentlich muss ich damit wieder aufhören.
‫Schauen wir also, was jetzt passiert.
‫Oh, wir haben hier bereits einen
‫Fehler, res. Status ist keine Funktion. Und
‫ja, eigentlich sein StatusCode. So schreibt man
‫es also in Express, also bin ich es schon gewohnt, Express so
‫zu schreiben, also ist Express ein Knoten. js-Framework, das wir im Rest des
‫Kurses verwenden werden, und in
‫Express machen Sie es so, und das
‫war das Problem. Ich bin also schon ein bisschen daran
‫gewöhnt, Express zu schreiben.
‫Gehen wir also zurück, und ja, jetzt sehen wir, dass die Datei
‫nicht gefunden wurde, und wenn wir die Entwicklungstools öffnen, sehen Sie unseren 500-Fehlercode, den wir
‫gerade gesendet haben, und wenn wir zu unserem Netzwerk-Tab gehen, versuchen
‫wir es noch einmal. Sie haben hier auch den Statuscode.
‫Also genau wie wir es in einem der vorherigen Videos in einem
‫anderen Abschnitt gesehen haben. So können wir diese
‫Art von Dingen auf der Registerkarte "Netzwerk" überprüfen.
‫In Ordnung, also lass es hier reparieren, speichern und okay, also
‫funktioniert das wieder perfekt, aber es gibt immer noch ein Problem mit
‫diesem Ansatz, den ich dir gerade
‫gezeigt habe. Und das Problem ist,
‫dass unser lesbarer Stream, also derjenige, den wir
‫verwenden, um die Datei von der Festplatte zu lesen, viel schneller
‫ist, als das Ergebnis tatsächlich mit dem beschreibbaren Antwortstream
‫über das Netzwerk zu senden. Und dies wird den
‫Antwortstrom überfordern, der all diese eingehenden Daten nicht so schnell verarbeiten kann.
‫Und dieses Problem wird als Gegendruck bezeichnet.
‫Und es ist ein echtes Problem, das in echten Situationen auftreten kann.
‫In diesem Fall tritt also ein Rückstau auf, wenn die Antwort
‫die Daten nicht annähernd so schnell senden kann, wie sie sie aus
‫der Datei empfängt. Ist das sinnvoll?
‫Also müssen wir diese Lösung
‫reparieren und eine noch bessere finden.
‫Und so werden wir Lösung drei
‫erstellen, und das ist eigentlich
‫die letzte, OK? Also nicht mehr Lösungen als drei.
‫Das Geheimnis hier ist also, tatsächlich den Pfeifenoperator zu benutzen, den ich
‫im letzten Video erwähnt habe, okay?
‫Der Pipe-Operator ist also für alle lesbaren Streams verfügbar und ermöglicht es
‫uns, die Ausgabe eines lesbaren Streams direkt
‫in die Eingabe eines beschreibbaren Streams zu leiten, okay?
‫Und das wird dann das Problem des Rückstaus
‫beheben, da es automatisch die Geschwindigkeit der
‫eingehenden Daten und die Geschwindigkeit der ausgehenden Daten verarbeitet.
‫Okay, also holen wir uns hier unseren lesbaren Stream.
‫Okay, das ist unser lesbarer Stream, und
‫jetzt müssen wir nur noch unseren lesbaren Stream nehmen,
‫die Pipe-Methode verwenden und dann einen beschreibbaren Stream einfügen, und das
‫ist die Antwort. Und das ist es tatsächlich.
‫Das ist alles, was wir für diese Lösung tun müssen, okay?
‫Damit es immer so funktioniert, lass
‫es mich hier als Kommentar schreiben. Wir brauchen also im Grunde eine lesbare Quelle,
‫okay, das ist wieder nur, um es Ihnen zu erklären,
‫dann können wir die Pipe darauf verwenden, und hier
‫müssen wir ein beschreibbares Ziel angeben. Von hier kommen also unsere
‫Daten, und es muss ein lesbarer Stream
‫sein, und diese Daten können wir dann an ein beschreibbares Ziel weiterleiten.
‫In diesem Fall ist unser Ziel also die Antwort, okay?
‫Nun kann dieser Stream hier tatsächlich auch ein Duplex- oder ein
‫Transformationsstream sein, aber was zählt ist, dass wir in
‫den Stream schreiben können. Und Antwort ist
‫natürlich so ein Stream. Wir können also natürlich die
‫Antwort schreiben, die an den Client gesendet wird, okay?
‫Der Rohrbetreiber löst also automatisch das Problem
‫des Gegendrucks, das wir zuvor hatten.
‫Und es ist eigentlich auch eine viel
‫elegantere und unkompliziertere Lösung. Was wir hier zuvor
‫getan haben, war, Ihnen alle Möglichkeiten zu zeigen,
‫wie wir die Stream-Methoden und -Ereignisse verwenden können, um diese Art von
‫Lösung zu erstellen, und natürlich gibt es viele Anwendungsfälle,
‫aber bei einem Problem wie diesem ist die Pipe Betreiber
‫ist eigentlich die beste Lösung. Hinter den Kulissen macht es
‫hier eigentlich so etwas, aber
‫wieder viel einfacher für uns zu schreiben, weil
‫es all die Dinge intern hinter
‫den Kulissen behandelt. Ich glaube also, dass Pipe
‫hier tatsächlich der einfachste Weg ist, Streams zu konsumieren und zu
‫schreiben, es sei denn, wir brauchen, wie bereits erwähnt,
‫eine individuellere Lösung. Und dann müssen wir
‫diese komplizierteren Werkzeuge wie diese Ereignisse und Methoden verwenden, die
‫ich Ihnen zuvor gezeigt habe. In Ordnung, also
‫zum Abschluss, beenden wir den Prozess hier, starten ihn erneut und
‫sehen natürlich, ob er noch funktioniert, was er tut.
‫Und so wird unsere Arbeit hier erledigt. Damit sind wir mit diesem
‫Vortrag fertig und können direkt zum nächsten übergehen.