Flask 中使用 make_response 下载大文件

在Flask中,可以使用make_response函数来实现下载大文件的功能。具体怎么操作呢,以我具体示例来说,其实很简单。以下是一个简单的示例代码,演示如何在Flask应用中使用make_response来下载大文件:

1、问题背景

在使用 Flask 框架开发 web 应用程序时,如果需要提供大文件下载功能,可能会遇到内存溢出问题。以下代码展示了一个尝试下载大文件的示例:

python 复制代码
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

当文件较小时(小于 1GB),这段代码可以正常工作。但是,当文件较大时(大于 1GB),就会抛出 MemoryError 异常。这是因为将超过 2GB 的二进制数据存储在一个字符串中可能会导致内存不足。

2、解决方案

为了解决这个问题,可以使用流式传输的方式来下载大文件。流式传输允许将文件分块发送,这样就可以避免内存不足的问题。

2.1 使用 Flask 的 stream_with_context() 函数

Flask 提供了一个 stream_with_context() 函数,可以将一个生成器对象转换为一个 Response 对象。生成器对象可以逐块生成数据,这样就可以避免一次性将整个文件加载到内存中。

以下代码展示了一个使用 stream_with_context() 函数的示例:

python 复制代码
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()))

在这个示例中,generate() 函数是一个生成器函数,它逐块生成数据。stream_with_context() 函数将这个生成器对象转换为一个 Response 对象,这样就可以将数据流式传输到客户端。

2.2 使用 Flask 的 send_from_directory() 函数

如果要下载的静态文件,可以使用 Flask 的 send_from_directory() 函数。这个函数可以将静态文件直接从文件系统中发送到客户端,这样就可以避免内存不足的问题。

以下代码展示了一个使用 send_from_directory() 函数的示例:

python 复制代码
from flask import send_from_directory

@app.route('/download_file')
def download_file():
    file_path = 'path/to/file.txt'
    return send_from_directory(os.path.dirname(file_path), os.path.basename(file_path))

在这个示例中,send_from_directory() 函数将文件 /path/to/file.txt 发送到客户端。

2.3 使用第三方库

也可以使用一些第三方库来实现大文件下载功能。例如,flask-large-file-downloader 库可以帮助你轻松下载大文件。

以下代码展示了一个使用 flask-large-file-downloader 库的示例:

python 复制代码
from flask_large_file_downloader import LargeFileDownloader

app = Flask(__name__)
app.secret_key = 'super secret key'

app.config['LARGE_FILE_DOWNLOADER_DIRECTORY'] = 'path/to/directory'
app.config['LARGE_FILE_DOWNLOADER_BUFFER_SIZE'] = 1024 * 1024

lf_downloader = LargeFileDownloader(app)

@app.route('/download_file')
def download_file():
    file_path = 'path/to/file.txt'
    return lf_downloader.send_file(file_path)

在这个示例中,lf_downloader.send_file() 函数将文件 /path/to/file.txt 发送到客户端。

通过设置适当的响应头信息,浏览器会提示用户下载文件。generate函数会以流式方式逐块读取大文件内容,避免一次性加载整个文件到内存中。

请确保替换代码中的path_to_your_large_file为你实际的大文件路径。这样,当访问/download_large_file路由时,Flask应用会开始下载指定的大文件。如果有更好得建议可以评论区留言讨论。

相关推荐
兵慌码乱13 分钟前
基于 MediaPipe 与 PySide2 的手势交互音乐控制系统实现:轻量化视觉交互全流程解析
python·opencv·计算机视觉·人机交互·手势识别·mediapipe·pyside2
IT_陈寒1 小时前
Redis内存爆了,原来我漏掉了这个致命配置
前端·人工智能·后端
fliter2 小时前
最后一块拼图:用 bitvec 构造 IPv4 包,真正做出自己的 Ping
后端
luckdewei3 小时前
FastAPI 资产管理系统实战:复杂 ORM 关联、Alembic 迁移与 N+1 查询优化
python
fliter3 小时前
用 Rust 解析并生成 ICMP 包:checksum、nom 与 cookie-factory
后端
蝎子莱莱爱打怪3 小时前
XZLL-IM干货系列 03|消息 ID 设计:一个 UUID 搞不定的事,我用两个 ID 解决了
后端·面试·开源
fliter3 小时前
从 panic 到 Result:用 Rust 重新整理一个 ping 项目的错误处理
后端
森蓝情丶4 小时前
我给 AI 搭了个法庭:一个前端仔的 LangGraph 实战全记录
前端·后端
JensCS猿4 小时前
从 Spring Boot 回看 SSM 框架:手动挡与自动挡的驾驶哲学
后端
爱勇宝4 小时前
干了近 8 年,一夜之间被裁:AI 时代,程序员最该害怕的不是 AI
前端·后端·程序员