2012-11-14 22 views
24

Flaskのストリーミングの使い方を理解できないようです。ここに私のコードです:Pythonと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> 

私は半秒毎に変更ページのカウンターを見たいのですが。代わりに、私が得た最も近いものは、次の行の各番号を印刷するページです。

答えて

38

ページ上の既存のコンテンツを置き換えるには、javascriptが必要な場合があります。つまり、送信したり、リクエストを作成したり、長いポーリングやWebソケットなどを使用したりできます。多くの方法があります。ここにはserver send events

#!/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:接続が失われた場合、ブラウザは3秒で、デフォルトでは再接続

<!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> 

。何も送信しない場合、サーバーは404を返すか、次の要求に応答して'text/event-stream'コンテンツタイプ以外を送信することができます。サーバーにさらにデータがある場合でもクライアント側で停止するには、source.close()と呼ぶことができます。

注:ストリームが無限であることを意味していないならば、(無限技術)のテキストを置き換えるためにはJavaScriptスニペットを送って、例えば他の技術(ないSSE)を使用します。

#!/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 %} 
+0

あなたは(特に私はSSEのデモで遊んでいます)提供のデモは、単一のクライアントのために動作します。新しいブラウザウィンドウを開き、ページストリーミングデータにアクセスしようとすると、前のページを閉じるか停止するまで何も起こりません。そして、カウンターは0でバックアップを開始します。どのようにこれを再試行して、これにアクセスしようとするクライアントが、同じデータ/カウンターを見て、アプリケーションが開始されてからカウントアップするようにしますか?私はあなたが別のスレッドでカウンタを実行する必要があると仮定しているが、私はこれを実装する方法がわからない。 –

+1

@DavidMarx:少なくとも2つの質問があります:(1)フラスコ内の複数の同時クライアントをサポートする方法は? - 答え:どのようなwsgiアプリケーションでも同じように、guncorn(2)を使用して、複数のクライアントの同じカウンタにアクセスする方法を教えてください。 - あなたが単一のワーカーを想定している、例えばグローバルなイテレータを定義し、ループ内で 'next(it)'を呼び出すなど、任意のサーバプログラムの共有データへのアクセスを提供するのと同じ方法です。とにかく、これらは別々の質問です。特定の問題に固有の新しい質問をしてください。 – jfs

+0

ありがとうございます。私はここでより焦点を絞ったトピックで新しい質問を投稿しました:http://stackoverflow.com/questions/33877359/building-a-front-end-webapp-for-a-real-time-data-stream-in- python –

6

私はあなたがそのようなテンプレートを使用するつもりなら、あなたはここに与えられた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())) 
関連する問題