当我们要往客户端发送大量的数据,比如一个大文件时,将它保存在内存中再一次性发到客户端开销很大。比较好的方式是使用流,本篇就要介绍怎么在Flask中通过流的方式来将响应内容发送给客户端。此外,我们还会演示如何实现文件的上传功能,以及如何获取上传后的文件。

响应流的生成

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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”,它是”werkzeug.wrappers.Response”类的一个包装,它的初始化方法第一个参数就是我们定义的生成器函数,第二个参数指定了响应类型。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
# 流式渲染模板
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对象,这个我们在Jinja2系列中有介绍,然后调用Environment对象的”get_template()”方法来获得模板对象,再调用模板对象的”stream()”方法生成一个”StreamTemplate”的对象。这个对象实现了”__next__()”方法,可以作为一个生成器使用,如果你看了Jinja2的源码,你会发现模板对象的”stream()”方法的实现就是使用了yield表达式,所以原理同上例一样。另外,我们启用了缓存”enable_buffering()”来避免客户端发送过于频繁,其参数的默认值就是5。

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

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

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

另外注意,在生成器中是无法访问请求上下文的。不过Flask从版本0.9开始提供了”stream_with_context()”方法,它允许生成器在运行期间获取请求上下文:

1
2
3
4
5
6
7
8
9
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对象。

转载:

1、http://www.bjhee.com/flask-ad5.html

flask如何使模板返回大文件,又不消耗大量内存的更多相关文章

  1. python读取大文件的方法及mmap内存映射模块

    python计算文件的行数和读取某一行内容的实现方法 :最简单的办法是把文件读入一个大的列表中,然后统计列表的长度.如果文件的路径是以参数的形式filepath传递的,那么只用一行代码就可以完成我们的 ...

  2. ASP.NET Core下载大文件的实现

    当我们的ASP.NET Core网站需要支持下载大文件时,如果不做控制可能会导致用户在访问下载页面时发生无响应,使得浏览器崩溃.可以参考如下代码来避免这个问题. 关于此代码的几点说明: 将数据分成较小 ...

  3. 把大象装进冰箱:HTTP传输大文件的方法

    上次我们谈到了HTTP报文里的div,知道了HTTP可以传输很多种类的数据,不仅是文本,也能传输图片,音频和视频.   早期互联网上传输的基本上都是只有几k大小的文本和小图片,现在的情况则大有不同.网 ...

  4. HttpApplication实战大文件上传 (第四篇)

    一.Asp.net中的文件上传 在Asp.net 1.1中,文件在上传过程中将被全部保存在内存中,对于大文件来说,会造成内存空间的过度使用,可能会招致恶意攻击.为了解决这个问题,Asp.net在配置文 ...

  5. java处理excel-xlsx格式大文件的解决方案

    1.第一次读取7M左右的ecxel文件,使用poi 库实现,参考了下面的博文. http://www.cnblogs.com/chenfool/p/3632642.html 使用上面的方法在 下面Wo ...

  6. python处理大文件——文件流处理

    最近处理一份1000G+的大文件,直接loading进内存不可能,只能分片读取.文件介绍如下: 该文件是一份压缩的比对后文件(sam文件),该文件由很多细小的结构单元组成,一个结构如下: 两种方法: ...

  7. flask之jinja2模板语言

    一.jinja2简单介绍 Jinja2是Python里一个被广泛应用的模版引擎,他的设计思想来源于Django的模板引擎,并扩展了其语法和一系列强大的功能.其中最显著的一个是增加了沙箱执行功能和可选的 ...

  8. PHP如何快速读取大文件

    在PHP中,对于文件的读取时,最快捷的方式莫过于使用一些诸如file.file_get_contents之类的函数,简简单单的几行代码就能 很漂亮的完成我们所需要的功能.但当所操作的文件是一个比较大的 ...

  9. php操作大文件

    看了http://hi.baidu.com/qiaoyuetian/item/76c51f0ce25030e4f45ba69e(php读取大文件详解),然后测试了里边的代码,发现一些错误, 总结,红色 ...

随机推荐

  1. [POJ] 3362 Telephone Lines

    Telephone Lines Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 7978 Accepted: 2885 Descr ...

  2. PHP+Mysql实现分页

    我们在项目开发的过程中避免不了使用分页功能,拿php来说,现在市面上有很多大大小小的php框架,当然了分页这种小功能这些框架中都是拿来直接可以用的. 这些框架的分页功能使用都很方便,配置一下分页所需参 ...

  3. linux文件权限更改命令chmod及数字权限

    chmod -change file mode bits :更改文件权限 chmod是用来改变文件或者目录权限的命令,但只有文件的属主和超级用户(root)才有这种权限. 更改文件权限的2种方式: 一 ...

  4. Python9-面对对象2-day23

    #计算正方形的周长和面积 class Square: def __init__(self,side_len): self.side_len = side_len def perimeter(self) ...

  5. Hive 启动报错 URI

    Exception in thread "main"java.lang.RuntimeException: java.lang.IllegalArgumentException:j ...

  6. Python算法-二叉树深度优先遍历

    二叉树 组成: 1.根节点  BinaryTree:root 2.每一个节点,都有左子节点和右子节点(可以为空)  TreeNode:value.left.right 二叉树的遍历: 遍历二叉树:深度 ...

  7. WordPress的编译器功能扩展

    //php代码如下://向文章编辑器的Visual区添加自定义按钮,js文件存放在wp-content/plugins/文件夹下 add_action('admin_head', 'my_custom ...

  8. Leetcode 407.接雨水

    接雨水 给定一个 m x n 的矩阵,其中的值均为正整数,代表二维高度图每个单元的高度,请计算图中形状最多能接多少体积的雨水. 说明: m 和 n 都是小于110的整数.每一个单位的高度都大于0 且小 ...

  9. Thread.getContextClassLoader() is null

    Java threads created from JNI code in a non-java thread have null ContextClassloader unless the crea ...

  10. 【字符串】BNUOJ 52781 Book Borders

    https://www.bnuoj.com/v3/problem_show.php?pid=52781 [AC] #include<bits/stdc++.h> using namespa ...