流式内容

有时您希望向客户端发送大量数据,远超您想要保存在内存中的数据量。当您动态生成数据时,如何在不往返文件系统的情况下将其发送回客户端?

答案是使用生成器和直接响应。

基本用法

这是一个基本的视图函数,它可以动态生成大量的 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()