Wie werden einfach Daten an das Antwortobjekt weitergeleitet, um Daten an den Client zu übertragen?

9

Im Beispielcode in diesem Artikel , wie ist es das letzte Segment des Streams, das auf der Linie arbeitet:

%Vor%

Ich verstehe, dass der erste Teil die Datei liest, der zweite Teil es komprimiert, aber was ist .pipe(res) ? das scheint die Aufgabe zu erledigen, die ich normalerweise mit res.send oder res.sendFile machen würde.

Vollständiger Code :

%Vor%
  

localhost:5000/files/test.txt => Browser displays text contents of that file

Wie werden die Daten einfach an das Antwortobjekt weitergeleitet, um sie an den Client zurückzugeben?

† was ich leicht geändert habe, um Express und ein paar andere kleinere Dinge zu benutzen.

    
1252748 31.12.2017, 01:06
quelle

4 Antworten

4
  

"Wie können die Daten einfach an das Antwortobjekt weitergeleitet werden, um die Daten an den Client zurückzugeben?"

Der Wortlaut von "das Antwortobjekt" in der Frage könnte bedeuten, dass der Fragesteller versucht, zu verstehen, warum Piping-Daten von einem Stream zu res irgendetwas tun . Das Missverständnis ist, dass res nur ein Objekt ist.

Dies liegt daran, dass alle Antworten ( res ) von http.ServerResponse ( in dieser Zeile ), was ein beschreibbares Stream ist. Wenn also Daten in res geschrieben werden, werden die geschriebenen Daten von http.ServerResponse verarbeitet, was die geschriebenen Daten intern an den Client zurücksendet.

Intern schreibt res.send tatsächlich nur in den zugrunde liegenden Stream, den es darstellt (selbst). res.sendFile tatsächlich pipes die Daten Lese von der Datei zu sich selbst.

Falls der Vorgang des "Verrohrens" von Daten von einem Strom zu einem anderen unklar ist, siehe den Abschnitt unten.

Wenn stattdessen der Datenfluss von Datei zu Client dem Fragesteller nicht klar ist, dann ist dies eine separate Erklärung.

Ich würde sagen, der erste Schritt zum Verständnis dieser Linie besteht darin, sie in kleinere, verständlichere Fragmente aufzuteilen:

Zuerst wird fs.createReadStream verwendet, um einen lesbaren Stream von a zu erhalten Dateiinhalte.

%Vor%

Als Nächstes wird ein transformierter -Stream erstellt, der Daten in ein komprimiertes Format umwandelt und die Daten in fileStream sind "piped" (übergeben).

%Vor%

Schließlich werden die Daten, die den compressionStream (den Transformations-Stream) durchlaufen, in die Antwort piped, was auch ein schreibbarer Stream .

%Vor%

Der Prozess ist ziemlich einfach, wenn er visuell ausgelegt wird:

Der Datenfluss ist jetzt ganz einfach: Die Daten kommen zuerst aus einer Datei, über einen Komprimierer und schließlich zu der Antwort, die die Daten intern zurück zum Client sendet.

  

Warten Sie, aber wie wird die Komprimierung in den Antwortdatenstrom geleitet?

Die Antwort ist, dass pipe den Ziel -Stream zurückgibt. Das heißt, wenn Sie a.pipe(b) ausführen, erhalten Sie b zurück vom Methodenaufruf.

Nehmen Sie zum Beispiel die Zeile a.pipe(b).pipe(c) . Zuerst wird a.pipe(b) ausgewertet, wobei b zurückgegeben wird. Dann wird .pipe(c) für das Ergebnis von a.pipe(b) aufgerufen, was b ist und somit b.pipe(c) entspricht.

%Vor%

Die Formulierung "impliziert, dass die Daten an das Antwortobjekt weitergeleitet werden" in der Frage könnte auch dazu führen, dass der Fragesteller den Datenfluss nicht versteht und denkt, dass die Daten direkt von a nach c gehen. Stattdessen sollte das obige verdeutlichen, dass die Daten von a nach b , dann b nach c ; fileStream bis compressionStream , dann compressionStream bis res .

Eine Code-Analogie

Wenn der gesamte Prozess immer noch keinen Sinn ergibt, kann es nützlich sein, den Prozess ohne das Konzept der Ströme neu zu schreiben:

Zuerst werden die Daten aus der Datei gelesen.

%Vor%

Die fileContents werden dann komprimiert. Dies geschieht mit einigen compress Funktion.

%Vor%

Abschließend werden die Daten über die Antwort res an den Client gesendet.

%Vor%

Die ursprüngliche Codezeile in der Frage und der obige Prozess sind mehr oder weniger die gleichen, abgesehen von der Einbeziehung von Streams in das Original.

Die Übernahme einiger Daten von einer externen Quelle ( fs.readFileSync ) ist wie eine lesbare Stream . Der Vorgang der Transformation der Daten ( compress ) über eine Funktion ist wie eine Transformation Stream . Das Senden der Daten an eine externe Quelle ( res.send ) ist wie eine schreibbare Stream .

"Streams sind verwirrend"

Wenn Sie verwirrt sind, wie Streams funktionieren, hier ist eine einfache Analogie: Jede Art von Stream kann im Kontext von Wasser (Daten) gedacht werden, die die Seite eines Berges von einem See auf der Spitze fließt. p>

  • Lesbare Streams sind wie der See oben, die Quelle des Wassers (Daten).
  • Beschreibbare Streams sind wie Menschen oder Pflanzen am Fuß des Berges und verbrauchen das Wasser (Daten).
  • Duplex Streams sind nur Streams, die sowohl lesbar als auch schreibbar sind. Sie sind mit einer Einrichtung am Boden verwandt, die Wasser aufnimmt und irgendeine Art von Produkt (d. H. Gereinigtes Wasser, kohlensäurehaltiges Wasser, etc.) ausgibt.
  • Transform -Streams sind auch Duplex-Streams. Sie sind wie Felsen oder Bäume an der Seite des Berges und zwingen das Wasser (Daten), einen anderen Weg zu nehmen, um auf den Grund zu kommen.

Eine bequeme Möglichkeit, alle Daten, die aus einem lesbaren Stream gelesen werden, direkt in einen schreibbaren Stream zu schreiben, ist nur pipe it , was den See direkt mit den Leuten verbindet.

%Vor%

Dies steht im Gegensatz zum Lesen von Daten aus dem lesbaren Stream und dann zum manuellen Schreiben in den schreibbaren Datenstrom:

%Vor%

Sie könnten sofort fragen, warum Transformations-Streams den Duplex-Streams entsprechen. Der einzige Unterschied zwischen den beiden ist, wie sie implementiert sind.

Transform-Streams implementieren eine _transform -Funktion, die geschriebene Daten aufnehmen und lesbare Daten zurückgeben soll, während ein Duplex-Stream einfach sowohl ein lesbarer als auch ein beschreibbarer Stream ist und daher _read und _write implementieren muss.

    
Clavin 09.01.2018 13:02
quelle
0

Ich bin mir nicht sicher, ob ich deine Frage richtig verstehe. Aber ich werde versuchen, den Code fs.createReadStream(filePath).pipe(brotli()).pipe(res) zu erklären, der hoffentlich deine Zweifel klären könnte.

Wenn Sie den Quellcode von iltorb überprüfen, gibt compressStream ein Objekt zurück von TransformStreamEncode , was Transform erweitert. Wie Sie sehen können, implementieren Transformations-Streams sowohl die lesbaren als auch die schreibbaren Schnittstellen. Wenn also fs.createReadStream(filePath).pipe(brotli()) ausgeführt wird, wird die beschreibbare Schnittstelle TransformStreamEncode verwendet, um die aus filePath gelesenen Daten zu schreiben. Wenn nun der nächste Aufruf von .pipe(res) ausgeführt wird, wird die lesbare Schnittstelle von TransformStreamEncode verwendet, um die komprimierten Daten zu lesen, und sie wird an res übergeben. Wenn Sie die Dokumentation des HTTP-Antwort -Objekts überprüfen, wird die Writable-Schnittstelle implementiert. Daher behandelt es intern das Ereignis pipe , um die komprimierten Daten von Readable TransformStreamEncode zu lesen, und sendet es dann an den Client.

HTH.

    
Vivek Athalye 02.01.2018 18:02
quelle
0

Sie fragen:

  

Wie können die Daten einfach an das Antwortobjekt weitergeleitet werden, um die Daten an den Client zurückzugeben?

Die meisten Leute verstehen "render X" als "produziere eine visuelle Darstellung von X". Das Senden der Daten an den Browser (hier durch Piping) ist ein notwendiger Schritt vor dem Rendern im Browser der Datei, die aus dem Dateisystem gelesen wird, aber das Piping ist nicht das, was das Rendern macht. Was passiert ist, dass die Express-App den Inhalt der Datei übernimmt, komprimiert und den komprimierten Stream unverändert an den Browser sendet. Dies ist ein notwendiger Schritt, da der Browser nichts rendern kann, wenn er nicht über die Daten verfügt. So wird .pipe nur verwendet, um die Daten an die Antwort zu übergeben, die an den Browser gesendet wird.

Es wird nicht selbst "gerendert" und dem Browser nicht mitgeteilt, was mit den Daten geschehen soll. Vor der Verrohrung passiert das: res.setHeader('Content-Type', 'text/html') . Der Browser sieht also eine Kopfzeile, die darauf hinweist, dass der Inhalt HTML ist. Browser wissen, was mit HTML zu tun ist: Zeigen Sie es an. Also wird es die Daten, die es bekommt, entpacken (weil der Header Content-Encoding sagt, dass es komprimiert ist), es als HTML interpretieren und es dem Benutzer zeigen, das heißt es rendern.

  

Was ist .pipe(res) ? das scheint die Aufgabe zu erledigen, die ich normalerweise mit res.send oder res.sendFile machen würde.

.pipe wird verwendet, um den gesamten Inhalt eines lesbaren Streams an einen beschreibbaren Stream zu übergeben. Es ist eine bequeme Methode beim Umgang mit Streams. Die Verwendung von .pipe zum Senden einer Antwort ist sinnvoll, wenn Sie aus einem Stream lesen müssen, um die Daten zu erhalten, die Sie in die Antwort aufnehmen möchten. Wenn Sie nicht aus einem Stream lesen müssen, sollten Sie .send oder .sendFile verwenden. Sie führen nette Buchhaltungsaufgaben aus, wie zum Beispiel den Header Content-Length , den Sie sonst selbst erledigen müssten.

Tatsächlich ist das Beispiel, das Sie zeigen, ein schlechter Versuch, eine Inhaltsverhandlung durchzuführen. Dieser Code sollte so umgeschrieben werden, dass res.sendFile verwendet wird, um die Datei an den Browser zu senden. Die Verarbeitung der Komprimierung sollte durch eine Middleware erfolgen, die für die Inhaltsverhandlung vorgesehen ist, da es viel mehr gibt, als nur das br -Schema zu unterstützen / p>     

Louis 04.01.2018 12:17
quelle
0

Lesen Sie dies, um die Antwort zu erhalten: Node.js Streams: Alles, was Sie tun muss es wissen

Ich zitiere interressanten Teil:

%Vor%

also fs.createReadStream(filePath).pipe(brotli()).pipe(res) entspricht var readableStream = fs.createReadStream(filePath).pipe(brotli());readableStream .pipe(res) und

%Vor%

, damit Node.js die Datei liest und in das lesbare Stream-Objekt fs.createReadStream(filePath) konvertiert. Dann gebe ich es zur ilistorbibliothek, die einen anderen lesbaren Stream .pipe(brotli()) (der den komprimierten Inhalt enthält) erstellt und schließlich den Inhalt an res übergibt, was beschreibbarer Stream ist. So ruft nodejs intern res.write() auf, die zurück in den Browser schreiben.

    
Et7f3XIV 09.01.2018 11:22
quelle

Tags und Links