• en
  • Language: ru
  • Documentation version: 1.1.x

Потоковое содержимое

Иногда вы хотите отправить клиенту огромное количество данных, гораздо больше, чем вы хотите хранить в памяти. Однако, когда вы генерируете данные на лету, как отправить их обратно клиенту, не обращаясь к файловой системе?

Ответ - с помощью генераторов и прямых ответов.

Основное использование

Это базовая функция представления, которая генерирует большое количество данных 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(). Затем шаблон оценивается по мере итерации потока. Поскольку при каждом выходе сервер будет передавать содержимое клиенту, вы можете захотеть буферизировать несколько элементов в шаблоне, что можно сделать с помощью 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.