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