Flask下载动态生成文件加一个简单的进度条

前一段码代码的时候碰见了这么一个情况,用户需要点击一个button然后下载一个比较大的动态生成的文件。flask里有一个send_file方法,可以把一个文件返回给客户端。可以先把动态生成的内容写到文件中,然后再用发送静态文件的方法,用send_file方法把这个文件返回,然后再把这个文件删除。不过这样的话就多了磁盘IO这一段时间,感觉很多此一举。

然后看了看send_file方法的代码,实际上是调用了一个FileWrapper的类,返回一个iterator,然后迭代返回块(默认的大小是4KB),和普通的Streaming的方法也没什么不一样的,只是包装了一下。所以直接用生成器构造Response即可。代码如下,关键的地方就只有添加一个Content-Length头,客户端每隔一段时间就可以计算一下当前的下载进度。

from flask import Flask
from flask import Response
from flask import render_template
from flask import make_response
from gevent.pywsgi import WSGIServer
app = Flask(__name__)
app.template_folder = "."

@app.route("/generate-on-fly")
def gen_on_fly():
def gen():
for i in xrange(200000):
yield "l"+"o"*100+"g\n"
resp = Response(gen())
resp.headers["Content-Length"] = resp.calculate_content_length()
return resp

@app.route("/")
def root():
resp = make_response(render_template("./root.html"))
resp.headers["Cache-Control"] = "no-store"
return resp

if __name__ == "__main__":
server = WSGIServer(('', 5000), app)
server.serve_forever()

根目录会返回一个html页面,页面上只有一个进度条和一个按钮。点击按钮会向/generate-on-fly发送请求,然后进度条开始滚动。root.html的内容如下。

<!DOCTYPE html>
<html>
<head>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
<script>
function updateProgress(evt)
{
var percentComplete = (evt.loaded / evt.total)*100;
$('#progressbar').attr("aria-valuenow", percentComplete);
$('#progressbar').attr("style", "width: "+percentComplete+"%;");
}
function sendreq(evt)
{
var req = new XMLHttpRequest();
req.onprogress = updateProgress;
req.open('GET', 'http://localhost:5000/generate-on-fly', true);
req.send();
}
</script>
</head>
<body>
<div class="row" style="margin: 30px">
<div class="col-lg-6">
<div class="progress">
<div id="progressbar" class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0"
aria-valuemax="100"
style="width: 0%;">
</div>
</div>
</div>
<button type="button" class="btn" onclick="sendreq()">Download</button>
</div>
</body>
</html>

过程大致就是每隔一段时间,就会进入req的onprogress绑定的回调,回调里面计算一下当前下载的百分比,然后更新一下进度条的状态,也不是很复杂。