2012-11-14 10 views
24

के साथ स्ट्रीमिंग डेटा मुझे लगता है कि फ्लास्क की स्ट्रीमिंग का उपयोग कैसे करें। यहाँ मेरी कोड है:पायथन और फ्लास्क

@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

पृष्ठ पर मौजूदा सामग्री को प्रतिस्थापित करने के लिए आपको जावास्क्रिप्ट की आवश्यकता हो सकती है यानी, आप इसे भेज सकते हैं या इसे आपके लिए अनुरोध कर सकते हैं, लंबे मतदान, websockets, आदि का उपयोग कर सकते हैं। ऐसा करने के कई तरीके हैं, यहां एक है जो 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:

<!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 स्निपेट भेज पाठ बदलने के लिए (अनंत तकनीक):

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

मैं inlined गए यह दिखाने के लिए यहां एचटीएमएल है कि इसमें कुछ और नहीं है (कोई जादू नहीं)। यहाँ ऊपर, लेकिन टेम्पलेट का उपयोग कर के रूप में ही है:

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

@ डेविड मैक्स: कम से कम दो प्रश्न हैं: (1) फ्लास्क में एकाधिक समवर्ती ग्राहकों का समर्थन कैसे करें? - उत्तर: वैसे ही आप इसे किसी भी wsgi ऐप के लिए करते हैं, उदाहरण के लिए, गनिकॉर्न (2) का उपयोग करें ताकि एकाधिक क्लाइंट के लिए एक ही काउंटर तक पहुंच प्रदान की जा सके? - वैसे ही आप किसी भी सर्वर प्रोग्राम में साझा डेटा तक पहुंच प्रदान करते हैं, उदाहरण के लिए, एक एकल कार्यकर्ता मानते हुए: वैश्विक पुनरावर्तक को परिभाषित करें और लूप में 'अगला (इसे)' कॉल करें। वैसे भी, ये अलग प्रश्न हैं। अपनी विशेष समस्या के लिए विशिष्ट एक नया प्रश्न पूछें। – jfs

+0

धन्यवाद जेएफ। मैंने यहां एक अधिक केंद्रित विषय के साथ एक नया प्रश्न पोस्ट किया है: http://stackoverflow.com/questions/33877359/building-a-front-end-webapp-for-a-real-time-data-stream-in- पायथन –

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())) 
संबंधित मुद्दे