|
| 1 | +# Transmitir datos { #stream-data } |
| 2 | + |
| 3 | +Si quieres transmitir datos que se puedan estructurar como JSON, deberías [Transmitir JSON Lines](../tutorial/stream-json-lines.md). |
| 4 | + |
| 5 | +Pero si quieres transmitir datos binarios puros o strings, aquí tienes cómo hacerlo. |
| 6 | + |
| 7 | +/// info | Información |
| 8 | + |
| 9 | +Añadido en FastAPI 0.134.0. |
| 10 | + |
| 11 | +/// |
| 12 | + |
| 13 | +## Casos de uso { #use-cases } |
| 14 | + |
| 15 | +Podrías usar esto si quieres transmitir strings puros, por ejemplo directamente de la salida de un servicio de AI LLM. |
| 16 | + |
| 17 | +También podrías usarlo para transmitir archivos binarios grandes, donde transmites cada bloque de datos a medida que lo lees, sin tener que leerlo todo en memoria de una sola vez. |
| 18 | + |
| 19 | +También podrías transmitir video o audio de esta manera; incluso podría generarse mientras lo procesas y lo envías. |
| 20 | + |
| 21 | +## Un `StreamingResponse` con `yield` { #a-streamingresponse-with-yield } |
| 22 | + |
| 23 | +Si declaras un `response_class=StreamingResponse` en tu *path operation function*, puedes usar `yield` para enviar cada bloque de datos a su vez. |
| 24 | + |
| 25 | +{* ../../docs_src/stream_data/tutorial001_py310.py ln[1:23] hl[20,23] *} |
| 26 | + |
| 27 | +FastAPI entregará cada bloque de datos a `StreamingResponse` tal cual, no intentará convertirlo a JSON ni nada parecido. |
| 28 | + |
| 29 | +### *path operation functions* no async { #non-async-path-operation-functions } |
| 30 | + |
| 31 | +También puedes usar funciones `def` normales (sin `async`) y usar `yield` de la misma manera. |
| 32 | + |
| 33 | +{* ../../docs_src/stream_data/tutorial001_py310.py ln[26:29] hl[27] *} |
| 34 | + |
| 35 | +### Sin anotación { #no-annotation } |
| 36 | + |
| 37 | +Realmente no necesitas declarar la anotación de tipo de retorno para transmitir datos binarios. |
| 38 | + |
| 39 | +Como FastAPI no intentará convertir los datos a JSON con Pydantic ni serializarlos de ninguna manera, en este caso la anotación de tipos es solo para que la use tu editor y tus herramientas; FastAPI no la usará. |
| 40 | + |
| 41 | +{* ../../docs_src/stream_data/tutorial001_py310.py ln[32:35] hl[33] *} |
| 42 | + |
| 43 | +Esto también significa que con `StreamingResponse` tienes la libertad y la responsabilidad de producir y codificar los bytes de datos exactamente como necesites enviarlos, independientemente de las anotaciones de tipos. 🤓 |
| 44 | + |
| 45 | +### Transmitir bytes { #stream-bytes } |
| 46 | + |
| 47 | +Uno de los casos de uso principales sería transmitir `bytes` en lugar de strings; por supuesto puedes hacerlo. |
| 48 | + |
| 49 | +{* ../../docs_src/stream_data/tutorial001_py310.py ln[44:47] hl[47] *} |
| 50 | + |
| 51 | +## Un `PNGStreamingResponse` personalizado { #a-custom-pngstreamingresponse } |
| 52 | + |
| 53 | +En los ejemplos anteriores, se transmitieron los bytes de datos, pero la response no tenía un header `Content-Type`, así que el cliente no sabía qué tipo de datos estaba recibiendo. |
| 54 | + |
| 55 | +Puedes crear una subclase personalizada de `StreamingResponse` que establezca el header `Content-Type` al tipo de datos que estás transmitiendo. |
| 56 | + |
| 57 | +Por ejemplo, puedes crear un `PNGStreamingResponse` que establezca el header `Content-Type` a `image/png` usando el atributo `media_type`: |
| 58 | + |
| 59 | +{* ../../docs_src/stream_data/tutorial002_py310.py ln[6,19:20] hl[20] *} |
| 60 | + |
| 61 | +Luego puedes usar esta nueva clase en `response_class=PNGStreamingResponse` en tu *path operation function*: |
| 62 | + |
| 63 | +{* ../../docs_src/stream_data/tutorial002_py310.py ln[23:27] hl[23] *} |
| 64 | + |
| 65 | +### Simular un archivo { #simulate-a-file } |
| 66 | + |
| 67 | +En este ejemplo estamos simulando un archivo con `io.BytesIO`, que es un objeto tipo archivo que vive solo en memoria, pero nos permite usar la misma interfaz. |
| 68 | + |
| 69 | +Por ejemplo, podemos iterarlo para consumir su contenido, como podríamos con un archivo. |
| 70 | + |
| 71 | +{* ../../docs_src/stream_data/tutorial002_py310.py ln[1:27] hl[3,12:13,25] *} |
| 72 | + |
| 73 | +/// note | Detalles técnicos |
| 74 | + |
| 75 | +Las otras dos variables, `image_base64` y `binary_image`, son una imagen codificada en Base64 y luego convertida a bytes, para después pasarla a `io.BytesIO`. |
| 76 | + |
| 77 | +Solo para que pueda vivir en el mismo archivo para este ejemplo y puedas copiarlo y ejecutarlo tal cual. 🥚 |
| 78 | + |
| 79 | +/// |
| 80 | + |
| 81 | +Al usar un bloque `with`, nos aseguramos de que el objeto tipo archivo se cierre cuando termine la función generadora (la función con `yield`). Es decir, después de que termine de enviar la response. |
| 82 | + |
| 83 | +No sería tan importante en este ejemplo específico porque es un archivo falso en memoria (con `io.BytesIO`), pero con un archivo real sí sería importante asegurarse de que el archivo se cierre al terminar de trabajar con él. |
| 84 | + |
| 85 | +### Archivos y async { #files-and-async } |
| 86 | + |
| 87 | +En la mayoría de los casos, los objetos tipo archivo no son compatibles con `async` y `await` por defecto. |
| 88 | + |
| 89 | +Por ejemplo, no tienen un `await file.read()`, ni un `async for chunk in file`. |
| 90 | + |
| 91 | +Y en muchos casos leerlos sería una operación bloqueante (que podría bloquear el event loop), porque se leen desde disco o desde la red. |
| 92 | + |
| 93 | +/// info | Información |
| 94 | + |
| 95 | +El ejemplo anterior es en realidad una excepción, porque el objeto `io.BytesIO` ya está en memoria, así que leerlo no bloqueará nada. |
| 96 | + |
| 97 | +Pero en muchos casos leer un archivo u objeto tipo archivo sí bloquearía. |
| 98 | + |
| 99 | +/// |
| 100 | + |
| 101 | +Para evitar bloquear el event loop, puedes simplemente declarar la *path operation function* con un `def` normal en lugar de `async def`; de esa forma FastAPI la ejecutará en un worker de threadpool para evitar bloquear el loop principal. |
| 102 | + |
| 103 | +{* ../../docs_src/stream_data/tutorial002_py310.py ln[30:34] hl[31] *} |
| 104 | + |
| 105 | +/// tip | Consejo |
| 106 | + |
| 107 | +Si necesitas llamar código bloqueante desde dentro de una función async, o una función async desde dentro de una función bloqueante, podrías usar [Asyncer](https://asyncer.tiangolo.com), un paquete hermano de FastAPI. |
| 108 | + |
| 109 | +/// |
| 110 | + |
| 111 | +### `yield from` { #yield-from } |
| 112 | + |
| 113 | +Cuando estés iterando sobre algo, como un objeto tipo archivo, y estés haciendo `yield` para cada elemento, también podrías usar `yield from` para hacer `yield` de cada elemento directamente y saltarte el `for`. |
| 114 | + |
| 115 | +Esto no es particular de FastAPI, es simplemente Python, pero es un truco útil que conviene conocer. 😎 |
| 116 | + |
| 117 | +{* ../../docs_src/stream_data/tutorial002_py310.py ln[37:40] hl[40] *} |
0 commit comments