Потоковая передача содержимого
Иногда вам нужно отправлять клиенту огромный объем данных, гораздо больший, чем вы хотите хранить в памяти. Но если вы генерируете данные на лету, как вы отправите их клиенту без обращения к файловой системе?
Ответ — с помощью генераторов и прямых ответов.
Основные принципы
Это простая функция представления, которая генерирует на лету большое количество данных в формате CSV. Секрет в том, чтобы иметь внутреннюю функцию, использующую генератор для генерации данных, и затем вызвать эту функцию и передать её результат объекту ответа:
from flask import Response
@app.route('/large.csv')
def generate_large_csv():
def generate():
for row in iter_all_rows():
yield ','.join(row) + '\n'
return Response(generate(), mimetype='text/csv')
Каждое yield выражение напрямую отправляется в браузер. Однако обратите внимание, что некоторые WSGI-мидлвары могут нарушить потоковую передачу, поэтому будьте осторожны в отладочных средах с профайлерами и другими включёнными инструментами.
Потоковая передача из шаблонов
Двигатель шаблонов Jinja2 также поддерживает рендеринг шаблонов по частям. Эта функциональность не предоставляется напрямую Flask, так как она довольно редкая, но вы легко можете реализовать её самостоятельно:
from flask import Response
def stream_template(template_name, **context):
app.update_template_context(context)
t = app.jinja_env.get_template(template_name)
rv = t.stream(context)
rv.enable_buffering(5)
return rv
@app.route('/my-large-page.html')
def render_large_template():
rows = iter_all_rows()
return Response(stream_template('the_template.html', rows=rows))
Секрет здесь в том, чтобы получить объект шаблона из окружения Jinja2 в приложении и вызвать stream() вместо render(), которая возвращает объект потока вместо строки. Поскольку мы обходим функции рендеринга шаблонов Flask и используем сам объект шаблона, нам нужно убедиться, что мы обновляем контекст рендеринга самостоятельно, вызывая update_template_context(). Шаблон затем оценивается по мере итерации по потоку. Поскольку каждый раз, когда вы выполняете yield, сервер будет очищать содержимое для клиента, вы можете захотеть буферизовать несколько элементов в шаблоне, что можно сделать с помощью rv.enable_buffering(size). 5 — разумное значение по умолчанию.
Потоковая передача с контекстом
Журнал изменений
Новое в версии 0.9.
Обратите внимание, что при потоковой передаче данных контекст запроса уже отсутствует в момент выполнения функции. Flask 0.9 предоставляет вам помощник, который может сохранить контекст запроса во время выполнения генератора:
from flask import stream_with_context, request, Response
@app.route('/stream')
def streamed_response():
def generate():
yield 'Hello '
yield request.args['name']
yield '!'
return Response(stream_with_context(generate()))
Без функции stream_with_context() вы получите RuntimeError в этот момент.
© 2007–2020 Pallets
Licensed under the BSD 3-clause License.
https://flask.palletsprojects.com/en/0.12.x/patterns/streaming/