当我们要往客户端发送大量的数据比较好的方式是使用流,通过流的方式来将响应内容发送给客户端,实现文件的上传功能,以及如何获取上传后的文件。

响应流的生成

Flask响应流的实现原理就是通过Python的生成器,也就是大家所熟知的yield的表达式,将yield的内容直接发送到客户端。下面就是一个简单的实现:

from flask import Flask, Response

app = Flask(__name__)

@app.route('/large.csv')
def generate_large_csv():
def generate():
for row in range(50000):
line = []
for col in range(500):
line.append(str(col)) if row % 1000 == 0:
print 'row: %d' % row
yield ','.join(line) + '\n' return Response(generate(), mimetype='text/csv')

这段代码会生成一个5万行100M的csv文件,每一行会通过yield表达式分别发送给客户端。

运行时你会发现文件行的生成与浏览器文件的下载是同时进行的,而不是文件全部生成完毕后再开始下载

这里我们用到了响应类”flask.Response”,它的初始化方法第一个参数就是我们定义的生成器函数,第二个参数指定了响应类型

我们将上述方法应用到模板中,如果模板的内容很大,怎么采用流的方式呢?这里我们要自己写个流式渲染模板的方法。

# 流式渲染模板
def stream_template(template_name, **context):
# 将app中的请求上下文内容更新至传入的上下文对象context,
# 这样确保请求上下文会传入即将被渲染的模板中
app.update_template_context(context)
# 获取Jinja2的模板对象
template = app.jinja_env.get_template(template_name)
# 获取流式渲染模板的生成器
generator = template.stream(context)
# 启用缓存,这样不会每一条都发送,而是缓存满了再发送
generator.enable_buffering(5) return generator

这段代码的核心,就是通过”app.jinja_env”来访问Jinja2的Environment对象,然后调用Environment对象的”get_template()”方法来获得模板对象,再调用模板对象的”stream()”方法生成一个”StreamTemplate”的对象。这个对象实现了”__next__()”方法,可以作为一个生成器使用,如果你看了Jinja2的源码,你会发现模板对象的”stream()”方法的实现就是使用了yield表达式,所以原理同上例一样。另外,我们启用了缓存”enable_buffering()”来避免客户端发送过于频繁,其参数的默认值就是5。

现在我们就可以在视图方法中,采用”stream_template()”,而不是以前介绍的”render_template()”来渲染模板了:

@app.route('/stream.html')
def render_large_template():
file = open('server.log')
return Response(stream_template('stream-view.html',logs=file.readlines()))

上例的代码会将本地的”server.log”日志文件内容传入模板,并以流的方式渲染在页面上。

”stream_with_context()”方法,它允许生成器在运行期间获取请求上下文:

from flask import request, stream_with_context

@app.route('/method')
def streamed_response():
def generate():
yield 'Request method is: '
yield request.method
yield '.'
return Response(stream_with_context(generate()))

因为我们初始化Response对象时调用了”stream_with_context()”方法,所以才能在yield表达式中访问request对象。

文件上传

我们分下面4个步骤来实现文件上传功能:

1、首先建立一个让用户上传文件的页面,我们将其放在模板”upload.html”中

<!DOCTYPE html>
<title>Upload File</title>
<h1>Upload new File</h1>
<form action="" method="post" enctype="multipart/form-data">
<p><input type="file" name="file">
<input type="submit" value="Upload">
 </p>
</form>

这里主要就是一个enctype=”multipart/form-data”的form表单;一个类型为file的input框,即文件选择框;还有一个提交按钮。

2、定义一个文件合法性检查函数

# 设置允许上传的文件类型
ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg']) # 检查文件类型是否合法
def allowed_file(filename):
# 判断文件的扩展名是否在配置项ALLOWED_EXTENSIONS中
return '.' in filename and \
filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS

3、文件提交后,在POST请求的视图函数中,通过request.files获取文件对象

这个request.files是一个字典,字典的键值就是之前模板中文件选择框的”name”属性的值,上例中是”file”;键值所对应的内容就是上传过来的文件对象。

4、检查文件对象的合法性后,通过文件对象的save()方法将文件保存在本地

我们将第3和第4步都放在视图函数中,代码如下:

import os
from flask import flask, render_template
from werkzeug import secure_filename app = Flask(__name__)
# 设置请求内容的大小限制,即限制了上传文件的大小
app.config['MAX_CONTENT_LENGTH'] = 5 * 1024 * 1024 # 设置上传文件存放的目录
UPLOAD_FOLDER = './uploads' @app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
# 获取上传过来的文件对象
file = request.files['file']
# 检查文件对象是否存在,且文件名合法
if file and allowed_file(file.filename):
# 去除文件名中不合法的内容
filename = secure_filename(file.filename)
# 将文件保存在本地UPLOAD_FOLDER目录下
file.save(os.path.join(UPLOAD_FOLDER, filename))
return 'Upload Successfully'
else: # 文件不合法
return 'Upload Failed'
else: # GET方法
return render_template('upload.html')
  • Flask的MAX_CONTENT_LENGTH配置项可以限制请求内容的大小,默认是没有限制,上例中我们设为5M。
  • 必须调用”werkzeug.secure_filename()”来使文件名安全
  • Flask处理文件上传的方式:如果上传的文件很小,那么会把它直接存在内存中。否则就会把它保存到一个临时目录下,通过”tempfile.gettempdir()”方法可以获取这个临时目录的位置。

一个简便的方法来让用户获取已上传的文件:

from flask import send_from_directory

@app.route('/uploads/<filename>')
def uploaded_file(filename):
return send_from_directory(UPLOAD_FOLDER, filename)

这个帮助方法”send_from_directory()”可以安全地将文件发送给客户端,它还可以接受一个参数”mimetype”来指定文件类型,和参数”as_attachment=True”来添加响应头”Content-Disposition: attachment”

Flask 文件和流的更多相关文章

  1. C#文件与流(FileStream、StreamWriter 、StreamReader 、File、FileInfo、Directory、directoryInfo、Path、Encoding)

    (FileStream.StreamWriter .StreamReader .File.FileInfo.Directory.DirectoryInfo.Path.Encoding)     C#文 ...

  2. 【转载】C++的文件和流

    http://www.iteedu.com/plang/ccpp/cppdxjch2b/111.php C++语言把每一个文件都看成一个有序的字节流(见图14.2),每一个文件或者以文件结束符(end ...

  3. Java - 文件(IO流)

    Java - 文件 (IO)   流的分类:     > 文件流:FileInputStream | FileOutputStream | FileReader | FileWriter     ...

  4. Java IO 文件与流基础

    Java IO 文件与流基础 @author ixenos 摘要:创建文件.文件过滤.流分类.流结构.常见流.文件流.字节数组流(缓冲区) 如何创建一个文件 #当我们调用File类的构造器时,仅仅是在 ...

  5. c# 文件与流

    1.创建和删除目录 在c#中涉及到输入.输出(i/o)相关操作的API都被放在System.IO命名空间下,或者子命令System.IO.IsolatedStoorage中.对目录进行操作可以使用Di ...

  6. C++ 文件和流

    到目前为止,我们已经使用了 iostream 标准库,它提供了 cin 和 cout 方法分别用于从标准输入读取流和向标准输出写入流. 本教程介绍如何从文件读取流和向文件写入流.这就需要用到 C++ ...

  7. netframework转core时文件响应流问题

    做将framework webapi项目转成netcore平台上的webapi项目时,发现原来的返回文件响应流在netcore平台下失效.代码如下,返回pdf文件响应流,供前端显示 /// <s ...

  8. (C/C++学习笔记) 二十. 文件和流

    二十. 文件和流 ● 文件的概念 文件(file) 一. C/C++语言将文件作为字节序列(sequence of characters)来对待,但从编码角度,或说从对字节信息的解释来看,文件分为:文 ...

  9. 进阶系列(4)—— C#文件与流

    一. 驱动器 在Windows操作系统中,存储介质统称为驱动器,硬盘由于可以划分为多个区域,每一个区域称为一个驱动器..NET Framew   ork提供DriveInfo类和 DriveType枚 ...

随机推荐

  1. 【BZOJ5093】图的价值(第二类斯特林数,组合数学,NTT)

    [BZOJ5093]图的价值(第二类斯特林数,组合数学,NTT) 题面 BZOJ 题解 单独考虑每一个点的贡献: 因为不知道它连了几条边,所以枚举一下 \[\sum_{i=0}^{n-1}C_{n-1 ...

  2. [HNOI2010]BUS 公交线路

    题面 Bzoj Sol 状压很显然 重点在于转移:题目就相当与每\(p\)长度的车站必须有且仅有\(k\)个被经过 那么转移时状压的二进制一定要有\(k\)个一 且两个相邻转移的状态之间必须满足:设为 ...

  3. iOS开发——iOS国际化 APP内语言切换

    最近一个一直在迭代的老项目收到一份新的开发需求,项目需要做国际化适配,简体中文+英文.由于项目中采用了storyboard和纯代码两种布局方式,所以国际化也要同时实现.上网查了些资料,实现了更改系统语 ...

  4. angularJs $mdDialog和$uibModal弹框关闭传值

    $mdDialog以一个点击button按钮出现弹框为例: $scope.btn=function($event,row){ var dScope = $scope.$new(true); dScop ...

  5. 24时区,GMT,UTC,DST,CST时间详解

    全球24个时区的划分      相较于两地时间表,可以显示世界各时区时间和地名的世界时区表(World Time),就显得精密与复杂多了,通常世界时区表的表盘上会标示着全球24个时区的城市名称,但究竟 ...

  6. 19.JavaScript

    简介 JavaScript一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型 1.注释 单行 // 多行 /* */ 2.引用方式 <head> <meta c ...

  7. Linux 下 vim 编辑文件,解决中文乱码,设置Tab键空格数

    vim编辑文件的时候,输入中文就出现乱码 解决办法: 以哪个用户登录的就在哪个用户目录下创建文件 vimrc vim .vimrc       (.创建的是隐藏文件) 文件内容: set tabsto ...

  8. 2018 年 3 月 iOS架构师 面试总结

    序言: 今年2月中下旬因为个人原因,换了一份工作,3月初期间面试了有3,4家,基本都是D轮或者刚刚上市的公司,也有上榜的BAT,也从他们的面试笔试中看到了自己的一些不足,于是就想写出来和大家分享一下, ...

  9. Java jsoup爬取图片

    jsoup爬取百度瀑布流图片 是的,Java也可以做网络爬虫,不仅可以爬静态网页的图片,也可以爬动态网页的图片,比如采用Ajax技术进行异步加载的百度瀑布流. 以前有写过用Java进行百度图片的抓取, ...

  10. js工具函数《转载收藏》

    1.等待所有图片加载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 var imgObjs = [], count = 0; rotate.prize ...