【问题标题】:Streaming data with Python and Flask使用 Python 和 Flask 流式传输数据
【发布时间】:2012-11-03 09:54:41
【问题描述】:

我似乎无法弄清楚如何使用 Flask 的流媒体。这是我的代码:

@app.route('/scans/')
def scans_query():
    url_for('static', filename='.*')
    def generate():
        yield render_template('scans.html')
        for i in xrange(50):
            sleep(.5)
            yield render_template('scans.html', **locals())
    return Response(stream_with_context(generate()))

在我的模板中:

<p>{% i %}</p>

我希望在页面上看到每半秒更改一次的计数器。相反,我得到的最接近的是在下一行打印出每个数字的页面。

【问题讨论】:

    标签: python flask


    【解决方案1】:

    我认为如果你要使用这样的模板,你可能需要使用这里给出的stream_template 函数:http://flask.pocoo.org/docs/patterns/streaming/#streaming-from-templates

    我没有对此进行测试,但它可能看起来像:

    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('/scans/')
    def scans_query():
        url_for('static', filename='.*')
        def generate():
            for i in xrange(50):
                sleep(.5)
                yield i
        return Response(stream_template('scans.html', i=generate()))
    

    【讨论】:

    • 使用此解决方案浏览 localhoast:5000/scans 在我的计算机上出现以下错误:jinja2.exceptions.TemplateSyntaxError: Encountered unknown tag 'i'. 有关如何成功运行它的任何建议?
    • @SpeedCoder5 这段代码是在 2012 年写的。我怀疑最近的 Flask 版本和 Python 版本需要不同的解决方案或至少有语法更改。
    【解决方案2】:

    要替换页面上的现有内容,您可能需要 javascript,即,您可以发送它或让它为您发出请求,使用长轮询、websockets 等。有很多方法可以做到这一点,这里有一个使用 @ 987654321@:

    #!/usr/bin/env python
    import itertools
    import time
    from flask import Flask, Response, redirect, request, url_for
    
    app = Flask(__name__)
    
    @app.route('/')
    def index():
        if request.headers.get('accept') == 'text/event-stream':
            def events():
                for i, c in enumerate(itertools.cycle('\|/-')):
                    yield "data: %s %d\n\n" % (c, i)
                    time.sleep(.1)  # an artificial delay
            return Response(events(), content_type='text/event-stream')
        return redirect(url_for('static', filename='index.html'))
    
    if __name__ == "__main__":
        app.run(host='localhost', port=23423)
    

    在哪里static/index.html

    <!doctype html>
    <title>Server Send Events Demo</title>
    <style>
      #data {
        text-align: center;
      }
    </style>
    <script src="http://code.jquery.com/jquery-latest.js"></script>
    <script>
    if (!!window.EventSource) {
      var source = new EventSource('/');
      source.onmessage = function(e) {
        $("#data").text(e.data);
      }
    }
    </script>
    <div id="data">nothing received yet</div>
    

    如果连接丢失,浏览器默认在 3 秒内重新连接。如果没有更多要发送的内容,服务器可以返回 404 或仅发送 'text/event-stream' 以外的内容类型以响应下一个请求。即使服务器有更多数据,要在客户端停止,您也可以调用source.close()

    注意:如果流不是无限的,则使用其他技术(不是 SSE),例如,发送 javascript sn-ps 来替换文本(无限 &lt;iframe&gt; 技术):

    #!/usr/bin/env python
    import time
    from flask import Flask, Response
    
    app = Flask(__name__)
    
    
    @app.route('/')
    def index():
        def g():
            yield """<!doctype html>
    <title>Send javascript snippets demo</title>
    <style>
      #data {
        text-align: center;
      }
    </style>
    <script src="http://code.jquery.com/jquery-latest.js"></script>
    <div id="data">nothing received yet</div>
    """
    
            for i, c in enumerate("hello"):
                yield """
    <script>
      $("#data").text("{i} {c}")
    </script>
    """.format(i=i, c=c)
                time.sleep(1)  # an artificial delay
        return Response(g())
    
    
    if __name__ == "__main__":
        app.run(host='localhost', port=23423)
    

    我在此处内联了 html,以表明它没有更多内容(没有魔法)。这里和上面一样,但是使用了模板:

    #!/usr/bin/env python
    import time
    from flask import Flask, Response
    
    app = Flask(__name__)
    
    
    def stream_template(template_name, **context):
        # http://flask.pocoo.org/docs/patterns/streaming/#streaming-from-templates
        app.update_template_context(context)
        t = app.jinja_env.get_template(template_name)
        rv = t.stream(context)
        # uncomment if you don't need immediate reaction
        ##rv.enable_buffering(5)
        return rv
    
    
    @app.route('/')
    def index():
        def g():
            for i, c in enumerate("hello"*10):
                time.sleep(.1)  # an artificial delay
                yield i, c
        return Response(stream_template('index.html', data=g()))
    
    
    if __name__ == "__main__":
        app.run(host='localhost', port=23423)
    

    在哪里templates/index.html

    <!doctype html>
    <title>Send javascript with template demo</title>
    <style>
      #data {
        text-align: center;
      }
    </style>
    <script src="http://code.jquery.com/jquery-latest.js"></script>
    <div id="data">nothing received yet</div>
    {% for i, c in data: %}
    <script>
      $("#data").text("{{ i }} {{ c }}")
    </script>
    {% endfor %}
    

    【讨论】:

    • 您提供的演示(特别是我正在玩 SSE 演示)仅适用于单个客户端。如果我打开一个新的浏览器窗口并尝试访问页面流数据,则在我关闭或停止上一页之前什么都不会发生。然后,计数器从 0 开始重新启动。您将如何修改它,以便任何尝试访问它的客户端都可以看到相同的数据/计数器,从应用程序启动的时间开始计数?我假设您必须在单独的线程中运行计数器,但我不确定如何实现。
    • @DavidMarx:至少有两个问题:(1)flask中如何支持多个并发客户端? — 答案:与任何 wsgi 应用程序一样,例如,使用 gunicorn (2) 如何为多个客户端提供对同一个柜台的访问权限? — 就像您在任何服务器程序中提供对共享数据的访问一样,例如,假设一个工作人员:定义全局迭代器并在循环中调用 next(it)。无论如何,这些都是单独的问题。针对您的特定问题提出一个新问题。
    • 尝试第一个示例读取日志文件并在 html 中显示,但 html 仅打印第一行.. 但 print() 标有 **** 打印所有行 def events(): with open('E :/Study/loggerApp/templates/job.log') as f: data=""; while True: data=f.read() yield "data: "+(data) if data!="": ****print(data)**** time.sleep(1) return Response(events() , content_type='文本/事件流')
    • @Karan:我很惊讶代码在 8 年后仍然有效。要发送多行数据,您可以尝试将每一行作为单独的事件发送,或者仅将数据作为 json 发送(在这种情况下,换行符被转义)。
    猜你喜欢
    • 2015-10-28
    • 1970-01-01
    • 1970-01-01
    • 2019-07-29
    • 2015-12-13
    • 1970-01-01
    • 1970-01-01
    • 2013-11-14
    • 1970-01-01
    相关资源
    最近更新 更多