Потоковое содержимое¶
Иногда вы хотите отправить клиенту огромное количество данных, гораздо больше, чем вы хотите хранить в памяти. Однако, когда вы генерируете данные на лету, как отправить их обратно клиенту, не обращаясь к файловой системе?
Ответ - с помощью генераторов и прямых ответов.
Основное использование¶
Это базовая функция представления, которая генерирует большое количество данных CSV на лету. Хитрость заключается в том, чтобы иметь внутреннюю функцию, которая использует генератор для генерации данных, а затем вызвать эту функцию и передать ее в объект ответа:
@app.route('/large.csv')
def generate_large_csv():
def generate():
for row in iter_all_rows():
yield f"{','.join(row)}\n"
return generate(), {"Content-Type": "text/csv"}
Каждое выражение yield
напрямую отправляется в браузер. Обратите внимание, что некоторые промежуточные модули WSGI могут нарушить потоковую передачу, поэтому будьте осторожны в отладочных средах с включенными профилировщиками и другими вещами.
Потоковая передача из шаблонов¶
Движок шаблонов Jinja2 поддерживает отрисовку шаблона по частям, возвращая итератор строк. Flask предоставляет функции stream_template()
и stream_template_string()
, чтобы упростить использование этой функции.
from flask import stream_template
@app.get("/timeline")
def timeline():
return stream_template("timeline.html")
Части, выдаваемые потоком рендеринга, как правило, совпадают с блоками утверждений в шаблоне.
Потоковая передача с контекстом¶
request
не будет активен во время работы генератора, поскольку в этот момент представление уже вернулось. Если вы попытаетесь получить доступ к request
, вы получите RuntimeError
.
Если ваша функция генератора полагается на данные в request
, используйте обертку stream_with_context()
. Это позволит сохранить контекст запроса активным во время работы генератора.
from flask import stream_with_context, request
from markupsafe import escape
@app.route('/stream')
def streamed_response():
def generate():
yield '<p>Hello '
yield escape(request.args['name'])
yield '!</p>'
return stream_with_context(generate())
Его также можно использовать в качестве декоративного средства.
@stream_with_context
def generate():
...
return generate()
Функции stream_template()
и stream_template_string()
автоматически используют stream_with_context()
, если запрос активен.