Flask 处理大文件的 make_response

23 投票
2 回答
36875 浏览
提问于 2025-04-18 10:26

我对文件输入输出和内存限制这些东西还不太熟悉,所以在用flask的 make_response 让我的网页应用成功下载大文件时遇到了困难。下面的代码在处理小文件(大约1GB以下)时可以正常工作,但当文件变大时,就会出现 MemoryError 的错误:

raw_bytes = ""
with open(file_path, 'rb') as r:
    for line in r:
        raw_bytes = raw_bytes + line
response = make_response(raw_bytes)
response.headers['Content-Type'] = "application/octet-stream"
response.headers['Content-Disposition'] = "inline; filename=" + file_name
return response

我猜把超过2GB的二进制数据放进一个字符串里肯定是不行的,但我不知道有什么其他方法可以实现文件下载。如果有人能给我一些关于如何用更有效的方式(比如分块下载或缓冲下载)来处理文件下载的建议,或者推荐一些中级的学习资源让我更深入理解这些内容,我会非常感激。谢谢!

2 个回答

5

你在尝试的过程中遇到的问题是,你首先把整个内容都读入了“raw_bytes”中,这样如果文件很大,就容易把你的内存用光。

解决这个问题有几种方法:

流式读取内容

正如davidism的回答所说,你可以使用一个生成器传递给Response。这样可以把大文件分成一块一块地读取,不需要占用太多内存。

流式读取不仅可以通过生成器实现,还可以直接从文件中读取,具体可以参考这个回答

通过Flask提供静态文件

如果你的文件是静态的,可以查找如何配置Flask来提供静态文件。这样文件会自动以流式的方式提供。

通过apachenginx(或其他网络服务器)提供静态文件

假设文件是静态的,在生产环境中,你应该通过反向代理来服务它,这样可以放轻你的Flask应用的负担,而且速度会更快。

35

可以查看关于流式内容的文档。简单来说,你需要写一个函数,这个函数会分块返回数据,然后把这个生成器传给响应,而不是一次性把所有数据都传过去。Flask和你的网络服务器会处理剩下的事情。

from flask import stream_with_context, Response

@app.route('/stream_data')
def stream_data():
    def generate():
        # create and return your data in small parts here
        for i in xrange(10000):
            yield str(i)

    return Response(stream_with_context(generate()))

如果文件是静态的,你可以利用send_from_directory()这个功能。文档建议你使用nginx或其他支持X-SendFile的服务器,这样读取和发送数据会更高效。

撰写回答