|
| 1 | +# Daten streamen { #stream-data } |
| 2 | + |
| 3 | +Wenn Sie Daten streamen möchten, die als JSON strukturiert werden können, sollten Sie [JSON Lines streamen](../tutorial/stream-json-lines.md). |
| 4 | + |
| 5 | +Wenn Sie jedoch **reine Binärdaten** oder Strings streamen möchten, so können Sie es machen. |
| 6 | + |
| 7 | +/// info | Info |
| 8 | + |
| 9 | +Hinzugefügt in FastAPI 0.134.0. |
| 10 | + |
| 11 | +/// |
| 12 | + |
| 13 | +## Anwendungsfälle { #use-cases } |
| 14 | + |
| 15 | +Sie könnten dies verwenden, wenn Sie reine Strings streamen möchten, z. B. direkt aus der Ausgabe eines **AI-LLM**-Dienstes. |
| 16 | + |
| 17 | +Sie könnten es auch nutzen, um **große Binärdateien** zu streamen, wobei Sie jeden Datenchunk beim Lesen streamen, ohne alles auf einmal in den Speicher laden zu müssen. |
| 18 | + |
| 19 | +Sie könnten auf diese Weise auch **Video** oder **Audio** streamen, es könnte sogar beim Verarbeiten erzeugt und gesendet werden. |
| 20 | + |
| 21 | +## Eine `StreamingResponse` mit `yield` { #a-streamingresponse-with-yield } |
| 22 | + |
| 23 | +Wenn Sie in Ihrer Pfadoperation-Funktion ein `response_class=StreamingResponse` deklarieren, können Sie `yield` verwenden, um nacheinander jeden Datenchunk zu senden. |
| 24 | + |
| 25 | +{* ../../docs_src/stream_data/tutorial001_py310.py ln[1:23] hl[20,23] *} |
| 26 | + |
| 27 | +FastAPI übergibt jeden Datenchunk unverändert an die `StreamingResponse`, es wird nicht versucht, ihn in JSON oder etwas Ähnliches zu konvertieren. |
| 28 | + |
| 29 | +### Nicht-async-Pfadoperation-Funktionen { #non-async-path-operation-functions } |
| 30 | + |
| 31 | +Sie können auch reguläre `def`-Funktionen (ohne `async`) verwenden und `yield` auf die gleiche Weise einsetzen. |
| 32 | + |
| 33 | +{* ../../docs_src/stream_data/tutorial001_py310.py ln[26:29] hl[27] *} |
| 34 | + |
| 35 | +### Keine Annotation { #no-annotation } |
| 36 | + |
| 37 | +Sie müssen den Rückgabetyp für das Streamen von Binärdaten nicht wirklich annotieren. |
| 38 | + |
| 39 | +Da FastAPI die Daten nicht mit Pydantic in JSON umzuwandeln oder sie anderweitig zu serialisieren versucht, ist die Typannotation hier nur für Ihren Editor und Tools relevant, sie wird von FastAPI nicht verwendet. |
| 40 | + |
| 41 | +{* ../../docs_src/stream_data/tutorial001_py310.py ln[32:35] hl[33] *} |
| 42 | + |
| 43 | +Das bedeutet auch, dass Sie mit `StreamingResponse` die **Freiheit** und **Verantwortung** haben, die Datenbytes genau so zu erzeugen und zu encodieren, wie sie gesendet werden sollen, unabhängig von den Typannotationen. 🤓 |
| 44 | + |
| 45 | +### Bytes streamen { #stream-bytes } |
| 46 | + |
| 47 | +Einer der Hauptanwendungsfälle wäre, `bytes` statt Strings zu streamen, das können Sie selbstverständlich tun. |
| 48 | + |
| 49 | +{* ../../docs_src/stream_data/tutorial001_py310.py ln[44:47] hl[47] *} |
| 50 | + |
| 51 | +## Eine benutzerdefinierte `PNGStreamingResponse` { #a-custom-pngstreamingresponse } |
| 52 | + |
| 53 | +In den obigen Beispielen wurden die Datenbytes gestreamt, aber die Response hatte keinen `Content-Type`-Header, sodass der Client nicht wusste, welchen Datentyp er erhielt. |
| 54 | + |
| 55 | +Sie können eine benutzerdefinierte Unterklasse von `StreamingResponse` erstellen, die den `Content-Type`-Header auf den Typ der gestreamten Daten setzt. |
| 56 | + |
| 57 | +Zum Beispiel können Sie eine `PNGStreamingResponse` erstellen, die den `Content-Type`-Header mit dem Attribut `media_type` auf `image/png` setzt: |
| 58 | + |
| 59 | +{* ../../docs_src/stream_data/tutorial002_py310.py ln[6,19:20] hl[20] *} |
| 60 | + |
| 61 | +Dann können Sie diese neue Klasse mit `response_class=PNGStreamingResponse` in Ihrer Pfadoperation-Funktion verwenden: |
| 62 | + |
| 63 | +{* ../../docs_src/stream_data/tutorial002_py310.py ln[23:27] hl[23] *} |
| 64 | + |
| 65 | +### Eine Datei simulieren { #simulate-a-file } |
| 66 | + |
| 67 | +In diesem Beispiel simulieren wir eine Datei mit `io.BytesIO`, einem dateiähnlichen Objekt, das nur im Speicher lebt, uns aber dieselbe Schnittstelle nutzen lässt. |
| 68 | + |
| 69 | +Wir können z. B. darüber iterieren, um seinen Inhalt zu konsumieren, so wie bei einer Datei. |
| 70 | + |
| 71 | +{* ../../docs_src/stream_data/tutorial002_py310.py ln[1:27] hl[3,12:13,25] *} |
| 72 | + |
| 73 | +/// note | Technische Details |
| 74 | + |
| 75 | +Die anderen beiden Variablen, `image_base64` und `binary_image`, sind ein in Base64 encodiertes Bild, dann in Bytes konvertiert, um es anschließend an `io.BytesIO` zu übergeben. |
| 76 | + |
| 77 | +Nur damit es in derselben Datei leben kann, für dieses Beispiel, und Sie es unverändert kopieren und ausführen können. 🥚 |
| 78 | + |
| 79 | +/// |
| 80 | + |
| 81 | +Mit einem `with`-Block stellen wir sicher, dass das dateiähnliche Objekt geschlossen wird, nachdem die Generatorfunktion (die Funktion mit `yield`) fertig ist. Also nachdem die Response gesendet wurde. |
| 82 | + |
| 83 | +In diesem speziellen Beispiel wäre das nicht so wichtig, weil es sich um eine unechte In-Memory-Datei (mit `io.BytesIO`) handelt, aber bei einer echten Datei wäre es wichtig sicherzustellen, dass die Datei nach der Arbeit damit geschlossen wird. |
| 84 | + |
| 85 | +### Dateien und Async { #files-and-async } |
| 86 | + |
| 87 | +In den meisten Fällen sind dateiähnliche Objekte standardmäßig nicht mit async und await kompatibel. |
| 88 | + |
| 89 | +Beispielsweise haben sie kein `await file.read()` oder `async for chunk in file`. |
| 90 | + |
| 91 | +Und in vielen Fällen wäre das Lesen eine blockierende Operation (die die Event-Loop blockieren könnte), weil von der Festplatte oder aus dem Netzwerk gelesen wird. |
| 92 | + |
| 93 | +/// info | Info |
| 94 | + |
| 95 | +Das obige Beispiel ist tatsächlich eine Ausnahme, weil sich das `io.BytesIO`-Objekt bereits im Speicher befindet, daher blockiert sein Lesen nichts. |
| 96 | + |
| 97 | +Aber in vielen Fällen würde das Lesen einer Datei oder eines dateiähnlichen Objekts blockieren. |
| 98 | + |
| 99 | +/// |
| 100 | + |
| 101 | +Um die Event-Loop nicht zu blockieren, können Sie die Pfadoperation-Funktion einfach mit normalem `def` statt `async def` deklarieren, dadurch führt FastAPI sie in einem Threadpool-Worker aus, um die Haupt-Event-Loop nicht zu blockieren. |
| 102 | + |
| 103 | +{* ../../docs_src/stream_data/tutorial002_py310.py ln[30:34] hl[31] *} |
| 104 | + |
| 105 | +/// tip | Tipp |
| 106 | + |
| 107 | +Wenn Sie blockierenden Code aus einer async-Funktion heraus aufrufen müssen, oder eine async-Funktion aus einer blockierenden Funktion, könnten Sie [Asyncer](https://asyncer.tiangolo.com), eine Schwesterbibliothek zu FastAPI, verwenden. |
| 108 | + |
| 109 | +/// |
| 110 | + |
| 111 | +### `yield from` { #yield-from } |
| 112 | + |
| 113 | +Wenn Sie über etwas iterieren, z. B. ein dateiähnliches Objekt, und dann für jedes Element `yield` verwenden, könnten Sie auch `yield from` verwenden, um jedes Element direkt weiterzugeben und die `for`-Schleife zu sparen. |
| 114 | + |
| 115 | +Das ist nichts Spezifisches an FastAPI, das ist einfach Python, aber ein netter Trick. 😎 |
| 116 | + |
| 117 | +{* ../../docs_src/stream_data/tutorial002_py310.py ln[37:40] hl[40] *} |
0 commit comments