1 changed files with 117 additions and 0 deletions
@ -0,0 +1,117 @@ |
|||||
|
# Потоковая передача данных { #stream-data } |
||||
|
|
||||
|
Если вы хотите передавать поток данных, который можно структурировать как JSON, используйте руководство [Stream JSON Lines](../tutorial/stream-json-lines.md){.internal-link target=_blank}. |
||||
|
|
||||
|
Но если вам нужно **передавать чистые бинарные данные** или строки, это можно сделать следующим образом. |
||||
|
|
||||
|
/// info |
||||
|
|
||||
|
Добавлено в FastAPI 0.134.0. |
||||
|
|
||||
|
/// |
||||
|
|
||||
|
## Варианты использования { #use-cases } |
||||
|
|
||||
|
Это может быть полезно, если вы хотите передавать чистые строки, например напрямую из вывода сервиса **AI LLM**. |
||||
|
|
||||
|
Также это подходит для передачи **больших бинарных файлов**, когда вы отправляете каждый фрагмент данных по мере чтения, не загружая весь файл в память целиком. |
||||
|
|
||||
|
Таким же способом можно передавать **видео** или **аудио**, включая случаи, когда они генерируются на лету во время обработки и отправки. |
||||
|
|
||||
|
## `StreamingResponse` с использованием `yield` { #a-streamingresponse-with-yield } |
||||
|
|
||||
|
Если в функции-обработчике пути вы объявите `response_class=StreamingResponse`, можно использовать `yield`, чтобы поочерёдно отправлять каждый фрагмент данных. |
||||
|
|
||||
|
{* ../../docs_src/stream_data/tutorial001_py310.py ln[1:23] hl[20,23] *} |
||||
|
|
||||
|
FastAPI будет передавать каждый фрагмент данных в `StreamingResponse` как есть — без попыток преобразовать его в JSON или выполнить иную сериализацию. |
||||
|
|
||||
|
### Неасинхронные функции-обработчики пути { #non-async-path-operation-functions } |
||||
|
|
||||
|
Можно также использовать обычные функции `def` (без `async`) и применять `yield` аналогичным образом. |
||||
|
|
||||
|
{* ../../docs_src/stream_data/tutorial001_py310.py ln[26:29] hl[27] *} |
||||
|
|
||||
|
### Без аннотации { #no-annotation } |
||||
|
|
||||
|
Для потоковой передачи бинарных данных необязательно объявлять аннотацию возвращаемого типа. |
||||
|
|
||||
|
Так как FastAPI не будет пытаться преобразовывать данные в JSON через Pydantic или сериализовать их каким-либо образом, аннотации типов в данном случае нужны только для вашего редактора кода и инструментов — сам FastAPI их использовать не будет. |
||||
|
|
||||
|
{* ../../docs_src/stream_data/tutorial001_py310.py ln[32:35] hl[33] *} |
||||
|
|
||||
|
Это означает, что при использовании `StreamingResponse` у вас есть **свобода** и одновременно **ответственность** формировать и кодировать байты данных именно так, как требуется для отправки, независимо от аннотаций типов. 🤓 |
||||
|
|
||||
|
### Передача байтов { #stream-bytes } |
||||
|
|
||||
|
Один из основных сценариев — передача объектов `bytes` вместо строк. Это полностью поддерживается. |
||||
|
|
||||
|
{* ../../docs_src/stream_data/tutorial001_py310.py ln[44:47] hl[47] *} |
||||
|
|
||||
|
## Пользовательский `PNGStreamingResponse` { #a-custom-pngstreamingresponse } |
||||
|
|
||||
|
В предыдущих примерах передавались байты данных, но HTTP-ответ не содержал HTTP-заголовка `Content-Type`, поэтому клиент не знал, какой тип данных получает. |
||||
|
|
||||
|
Можно создать собственный подкласс `StreamingResponse`, который устанавливает HTTP-заголовок `Content-Type` в соответствии с типом передаваемых данных. |
||||
|
|
||||
|
Например, можно создать `PNGStreamingResponse`, задав атрибут `media_type` со значением `image/png`: |
||||
|
|
||||
|
{* ../../docs_src/stream_data/tutorial002_py310.py ln[6,19:20] hl[20] *} |
||||
|
|
||||
|
Затем этот класс можно использовать в параметре `response_class=PNGStreamingResponse` в функции-обработчике пути: |
||||
|
|
||||
|
{* ../../docs_src/stream_data/tutorial002_py310.py ln[23:27] hl[23] *} |
||||
|
|
||||
|
### Имитация файла { #simulate-a-file } |
||||
|
|
||||
|
В этом примере используется `io.BytesIO` — файловоподобный объект, который существует только в памяти, но предоставляет тот же интерфейс. |
||||
|
|
||||
|
Например, его можно итерировать для чтения содержимого так же, как обычный файл. |
||||
|
|
||||
|
{* ../../docs_src/stream_data/tutorial002_py310.py ln[1:27] hl[3,12:13,25] *} |
||||
|
|
||||
|
/// note | Технические детали |
||||
|
|
||||
|
Две другие переменные — `image_base64` и `binary_image` — содержат изображение, закодированное в Base64, затем преобразованное в байты и переданное в `io.BytesIO`. |
||||
|
|
||||
|
Это сделано только для того, чтобы пример находился в одном файле и его можно было скопировать и запустить как есть. 🥚 |
||||
|
|
||||
|
/// |
||||
|
|
||||
|
Используя блок `with`, мы гарантируем, что файловоподобный объект будет закрыт после завершения генераторной функции (функции с `yield`), то есть после отправки HTTP-ответа. |
||||
|
|
||||
|
В данном примере это не критично, так как используется объект `io.BytesIO`, находящийся в памяти. Но при работе с реальным файлом важно убедиться, что он закрывается после завершения работы. |
||||
|
|
||||
|
### Файлы и async { #files-and-async } |
||||
|
|
||||
|
В большинстве случаев файловоподобные объекты по умолчанию не совместимы с `async` и `await`. |
||||
|
|
||||
|
Например, у них нет `await file.read()` или `async for chunk in file`. |
||||
|
|
||||
|
Кроме того, чтение может быть блокирующей операцией (например, при чтении с диска или из сети), что способно заблокировать цикл событий. |
||||
|
|
||||
|
/// info |
||||
|
|
||||
|
Пример выше является исключением, так как `io.BytesIO` уже находится в памяти, и его чтение не блокирует выполнение. |
||||
|
|
||||
|
Однако в большинстве случаев чтение файла или файловоподобного объекта будет блокирующим. |
||||
|
|
||||
|
/// |
||||
|
|
||||
|
Чтобы избежать блокировки цикла событий, можно объявить функцию-обработчик пути как обычную `def`, а не `async def`. В этом случае FastAPI выполнит её в воркер-процессе из пула потоков, чтобы не блокировать основной цикл. |
||||
|
|
||||
|
{* ../../docs_src/stream_data/tutorial002_py310.py ln[30:34] hl[31] *} |
||||
|
|
||||
|
/// tip |
||||
|
|
||||
|
Если вам нужно вызвать блокирующий код из асинхронной функции или асинхронную функцию из блокирующей, можно использовать библиотеку Asyncer — сопутствующую библиотеку для FastAPI. |
||||
|
|
||||
|
/// |
||||
|
|
||||
|
### `yield from` { #yield-from } |
||||
|
|
||||
|
Если вы итерируетесь по объекту (например, файловоподобному) и вызываете `yield` для каждого элемента, можно использовать `yield from`, чтобы напрямую передавать элементы без явного цикла `for`. |
||||
|
|
||||
|
Это относится не только к FastAPI, а к Python в целом, но приём полезный. 😎 |
||||
|
|
||||
|
{* ../../docs_src/stream_data/tutorial002_py310.py ln[37:40] hl[40] *} |
||||
Loading…
Reference in new issue