Flask: テキスト・ストリーミングやってみた♬

画像のStreaming配信で苦労しているが、テキスト・ストリーミング配信もそれなりに大変。。。

Flask簡単という噂なんだけど、やはりhtmlの知識が圧倒的に無いからだろうか？？
それにしても、皆さん苦労しているような気がする。
そしてなにより、情報がほとんどない。

ということで、今回は一応テキスト・ストリーミングができたので記事にしておく。

【参考】
１．公式サイト[Streaming Contents](http://flask.pocoo.org/docs/1.0/patterns/streaming/)
２．[huiliu/app.py ](https://gist.github.com/huiliu/46be335427605960fa84)

当初、１のサイトの記事見て、ほとんど意味不明。。。
そして、２のサイトのコード見て、できそうな予感。
しかし、vmax  commented on Mar 19 2017 さんのコメントが無ければできなかったか、さらに時間がかかったと思う。

ということで、早速解説しよう。
###Dir構成
```
flask_PJ
├── app_streaming.py
|── templates
   └── template.html
```

###コードは以下のとおり
[Video-Stream / app_stream.py ](https://github.com/MuAuan/Video-Stream/blob/master/app_stream.py)
[Video-Stream / templates / template.html ](https://github.com/MuAuan/Video-Stream/blob/master/templates/template.html)

###コード解説
正直に云うと、今回は中身をちゃんと理解していない。
それでも、ポイントだけは解説したいと思う。

```template.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Streaming Sample</title>
</head>
 <body>
  <h1>Hello !</h1>
    {% for item in rows %}
      <h1>{{ item }}</h1>
    {% endfor %}
 </body>
</html>
```
重要な部分は以下のとおりです。これは、上記参考２[huiliu/app.py ](https://gist.github.com/huiliu/46be335427605960fa84)のままであるが、一応、こんな感じという部分を追加してtemplate.htmlとしました。
ただ、飾りは何もなくても動きます。

```python
    {% for item in rows %}
      <h1>{{ item }}</h1>
    {% endfor %}
```
これは、rowsの要素がなくなるまで、itemに代入して表示します。
これ、縦にずらっと並びます。

```app_streaming.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from flask import Flask, stream_with_context, request, Response, flash
from flask import render_template, redirect
from time import sleep
import cv2

app = Flask(__name__)
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] 

@app.route('/')
def index():
    return redirect('/stream')

def stream_template(template_name, **context):
    app.update_template_context(context)
    t = app.jinja_env.get_template(template_name)
    rv = t.stream(context)
    rv.disable_buffering()
    return rv

def generate():
    while True:
        for item in data:
            yield str(item)
            sleep(1)
        if cv2.waitKey(100) >= 0:  
            break

@app.route('/stream')
def stream_view():
    rows = generate()
    return Response(stream_with_context(stream_template('template.html', rows=rows)))

if __name__ == '__main__':
    app.debug = True
    app.run()
```
    return Response(stream_with_context(stream_template('template.html', rows=rows)))
：このvmaxさんのコメントの理解。。これがどうもStreamingの肝みたいです。

    stream_with_context()関数
：[15. Stream Helpers](https://tedboy.github.io/flask/interface_api.stream_helpers.html)の説明によれば、「サーバーではレスポンスが開始すると情報が消えてしまうが、それを長く保持する役割」とのことで、使い方は二種類あるようです。rows = generate()なのでこういう使い方ですね。

    def stream_template(template_name, **context):
：stream_template関数でtemplate.htmlにrowsで得たyield str(item)つまりdata配列の数字をrv = t.stream(context)によって渡し、rv.disable_buffering()でこれ以上バッファリングしないこととし、rvをreturnしています。

###今回最大の成功は。。。
なんてことありませんが、redirect('/stream')でstream_veiw()に実行を移せたことです。
どうやったら、これを動かせるか頭真っ白だったので。。。

```python
@app.route('/')
def index():
    return redirect('/stream')
```
###まとめ
・テキスト・ストリーミングが実行できた
・ほぼ内容を理解できた

さて、画像やらないと。。しかし画像並んでもなぁ～

###実行例（横に出力した場合）
この出力例、なんとなく綺麗。。ウィンドウの横幅を変えると数字の並びが変わって法則性があって面白い。。。この例は縦列がバイファケーション（笑）
![hello_streaming.jpg](https://qiita-image-store.s3.amazonaws.com/0/233744/fc7a120d-1643-3eb6-a461-43323fb83976.jpeg)

###おまけ：公式サイトの例を動かしてみる
【参考】
１．公式サイト[Streaming Contents](http://flask.pocoo.org/docs/1.0/patterns/streaming/)
のコードを動かしてみた。

上のコードはほとんど今回の上記と同じなのでわかるとして、下のコードが簡単すぎてわからなかったが、以下のようにやると動くんですね。

```
http://127.0.0.1:5000/stream?name=MuAuan
```
ということで、まともなコードは以下のようになる(笑)

```streaming_hello.py
from flask import Flask, stream_with_context, request, Response, redirect,render_template
app = Flask(__name__)
   
@app.route("/")
def hello():
    return render_template('streaming_hello.html')

@app.route('/stream')
def streamed_response():
    def generate():
        yield 'Hello '
        yield request.args['name']
        yield '!'
    return Response(stream_with_context(generate()))

if __name__ == '__main__':
    app.debug = True
    app.run()
```

```streaming_hello.html
<form action="/stream" method="GET">
    <input name="name">
    <input type="submit" value="send">
</form>
```