引言

大家好,今天继续我们的"每天一个Python小技巧"系列。在Web开发中,处理大文件下载或实时数据推送时,传统的一次性响应往往会占用大量内存。本文将介绍如何使用Flask的Response对象结合g对象实现高效的流式输出。

一、什么是流式响应?

流式响应(Streaming Response)允许服务器将数据分块(chunk)发送给客户端,而不是等待所有数据准备好后一次性发送。这种方式特别适合:

大文件下载(如视频、日志文件)
实时数据推送(如大模型的推理过程)
长时间运行的任务进度报告

二、基础流式响应实现

我们先看一个最简单的流式响应示例:

from flask import Flask, Response
import time

app = Flask(__name__)

def generate_data():
    """模拟数据生成器"""
    for i in range(5):
        yield f"数据块 {i}\n"  # yield是关键!
        time.sleep(1)  # 模拟数据处理延迟

@app.route('/basic_stream', methods=['POST'])
def basic_stream():
    return Response(generate_data(), content_type='text/plain')

if __name__ == "__main__":
    # 运行 Flask 应用
    app.run(host='0.0.0.0', port=5007, debug=True)

"""
curl -X POST 127.0.0.1:5007/basic_stream
数据块 0
数据块 1
数据块 2
数据块 3
数据块 4
"""

调用这个接口(curl -X POST 127.0.0.1:5007/basic_stream),你会看到数据是逐块显示的,而不是一次性加载完成。

三、结合g对象增强流式响应

g对象是Flask提供的请求上下文全局变量,非常适合存储请求级别的数据。结合流式响应可以做到:

from flask import Flask, Response, g, stream_with_context
import time
import uuid
from datetime import datetime

app = Flask(__name__)

@app.before_request
def init_request():
    """请求预处理:初始化g对象数据"""
    g.request_id = str(uuid.uuid4())[:8]  # 生成简短请求ID
    g.start_time = datetime.now()  # 记录请求开始时间

def generate_enhanced_data():
    """增强版数据生成器,使用g对象数据"""
    yield f"请求 {g.request_id} 开始于 {g.start_time}\n"

    for i in range(1, 6):
        elapsed = (datetime.now() - g.start_time).total_seconds()
        yield f"区块 {i} | 已耗时: {elapsed:.2f}秒\n"
        time.sleep(0.8)  # 模拟处理延迟

    yield f"\n请求 {g.request_id} 处理完成!"

@app.route('/enhanced_stream', methods=['POST'])
def enhanced_stream():
    """注意必须使用stream_with_context包装生成器"""
    return Response(
        stream_with_context(generate_enhanced_data()),
        content_type='text/plain'
    )

if __name__ == "__main__":
    # 运行 Flask 应用
    app.run(host='0.0.0.0', port=5007, debug=True)
"""
curl -X POST 127.0.0.1:5007/enhanced_stream
请求 89ea7fa2 开始于 2025-04-25 09:26:12.200373
区块 1 | 已耗时: 0.00秒
区块 2 | 已耗时: 0.80秒
区块 3 | 已耗时: 1.61秒
区块 4 | 已耗时: 2.41秒
区块 5 | 已耗时: 3.21秒

请求 89ea7fa2 处理完成!
"""

关键点说明:

  • stream_with_context 必须使用,否则生成器内无法访问g对象
  • yield 是Python生成器关键语法
  • g对象自动保证线程安全,每个请求独立

四、常见问题解答

‌Q:为什么我的生成器内无法访问g对象?‌
A:必须使用stream_with_context()包装生成器函数

‌Q:流式响应会影响性能吗?‌
A:对于大数据量,流式响应会显著降低内存占用,但可能增加少量网络开销

‌Q:如何中断长时间运行的流式响应?‌
A:客户端断开连接时,生成器会自动停止,也可以通过信号量机制主动停止

结语

Flask的流式响应结合g对象,为我们处理大文件、实时数据等场景提供了优雅的解决方案。记住关键点:

  • 使用yield而非return
  • 必须用stream_with_context包装
  • g对象存储请求级别数据
Logo

永洪科技,致力于打造全球领先的数据技术厂商,具备从数据应用方案咨询、BI、AIGC智能分析、数字孪生、数据资产、数据治理、数据实施的端到端大数据价值服务能力。

更多推荐