Потоковая передача содержимого
Иногда вам нужно отправить клиенту огромное количество данных, намного больше, чем вы хотите хранить в памяти. Но если данные генерируются на лету, как их отправить клиенту без обращения к файловой системе?
Ответ — использовать генераторы и прямые ответы.
Основные принципы использования
Это простая функция представления, которая генерирует на лету большое количество данных в формате CSV. Хитрость заключается в том, чтобы иметь внутреннюю функцию, использующую генератор для генерации данных, а затем вызвать эту функцию и передать ее в объект ответа:
@app.route('/large.csv')
def generate_large_csv():
def generate():
for row in iter_all_rows():
yield f"{','.join(row)}\n"
return app.response_class(generate(), mimetype='text/csv')
Каждый yield выражение отправляется напрямую в браузер. Однако имейте в виду, что некоторые промежуточные ПО WSGI могут нарушить потоковую передачу, поэтому будьте осторожны в отладочных средах с профилировщиками и другими включенными инструментами.
Потоковая передача из шаблонов
Движок шаблонов Jinja2 также поддерживает рендеринг шаблонов по частям. Эта функциональность не экспортируется напрямую Flask, потому что она довольно редкая, но вы можете легко реализовать её самостоятельно:
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 app.response_class(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
@app.route('/stream')
def streamed_response():
def generate():
yield 'Hello '
yield request.args['name']
yield '!'
return app.response_class(stream_with_context(generate()))
Без функции stream_with_context() в этот момент вы получите RuntimeError.
© 2007–2021 Pallets
Licensed under the BSD 3-clause License.
https://flask.palletsprojects.com/en/2.0.x/patterns/streaming/