简体   繁体   English

使用Python和Flask流数据

[英]Streaming data with Python and Flask

I can't seem to figure out how to using Flask's streaming. 我似乎无法弄清楚如何使用Flask的流式传输。 Here's my code: 这是我的代码:

@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()))

and in my template: 在我的模板中:

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

I would like to see a counter on the page that changes every half second. 我希望页面上的计数器每半秒更改一次。 Instead, the closest I've gotten is the page printing out each number on the next line. 相反,我得到的最接近的页面是在下一行打印出每个数字。

To replace existing content on the page you might need javascript ie, you could send it or make it to make requests for you, use long polling, websockets, etc. There are many ways to do it, here's one that uses server send events : 要替换页面上的现有内容,您可能需要JavaScript,例如,您可以发送它或使其为您提出请求,使用长轮询,websockets等。有很多方法可以使用,这是一种使用服务器发送事件的方法

#!/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)

Where static/index.html : 其中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>

The browser reconnects by default in 3 seconds if the connection is lost. 如果连接断开,浏览器默认会在3秒内重新连接。 if there is nothing more to send the server could return 404 or just send some other than 'text/event-stream' content type in response to the next request. 如果没有其他要发送的内容,则服务器可以返回404或仅发送'text/event-stream'以外'text/event-stream'其他内容类型来响应下一个请求。 To stop on the client side even if the server has more data you could call source.close() . 要在客户端停止,即使服务器有更多数据,也可以调用source.close()

Note: if the stream is not meant to be infinite then use other techniques (not SSE) eg, send javascript snippets to replace the text (infinite <iframe> technique): 注意:如果流不是无限的,则使用其他技术(而非SSE),例如,发送javascript代码段替换文本(无限<iframe>技术):

#!/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)

I've inlined the html here to show that there is nothing more to it (no magic). 我已在此处内联html,以显示仅此而已(没有魔法)。 Here's the same as above but using templates: 与上面相同,但使用模板:

#!/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)

Where templates/index.html : 其中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 %}

I think if you're going to use templates like that, you might need to use the stream_template function given here: http://flask.pocoo.org/docs/patterns/streaming/#streaming-from-templates 我认为,如果您要使用这样的模板,则可能需要使用此处提供的stream_template函数: http : //flask.pocoo.org/docs/patterns/streaming/#streaming-from-templates

I didn't test this, but it might look like: 我没有对此进行测试,但可能看起来像:

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()))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM