流式传输内容

有时,您希望发送非常巨量的数据到客户端,远远超过您可以保存在内存中的量。 在您实时地产生这些数据时,如何才能直接把他发送给客户端,而不需要在文件 系统中中转呢?

答案是使用发电机和直接反应。

基本用法

下面是一个简单的视图函数,这一视图函数实时生成大量的 CSV 数据, 这一技巧使用了一个内部函数,这一函数使用生成器来生成数据,并且 稍后激发这个生成器函数时,把返回值传递给一个 response 对象: 诀窍是有一个内部函数使用生成器生成数据,然后调用该函数并将其传递给响应对象:

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