如何在Django中流式传输HttpResponse
我正在尝试让Django(1.2)中的“你好,世界”流式响应工作。我已经弄明白了如何使用生成器和yield
函数。但是,响应仍然没有流式传输。我怀疑可能有某个中间件在干扰它——也许是ETAG计算器?但我不太确定该怎么禁用它。有人能帮我一下吗?
这是我目前的流式响应“你好,世界”代码:
def stream_response(request):
resp = HttpResponse( stream_response_generator())
return resp
def stream_response_generator():
for x in range(1,11):
yield "%s\n" % x # Returns a chunk of the response to the browser
time.sleep(1)
2 个回答
很多 Django 的中间件会阻止你进行内容流式传输。如果你想使用 Django 的管理应用,这些中间件大部分是需要开启的,所以这可能会让人感到烦恼。不过,幸运的是,这个问题在 Django 1.5 版本中得到了改善。你可以使用 StreamingHttpResponse 来表示你想要流式返回结果,Django 自带的所有中间件都知道这一点,并会相应地处理,不会再缓存你的内容输出,而是直接发送给浏览器。你的代码可以像下面这样使用新的 StreamingHttpResponse 对象。
def stream_response(request):
return StreamingHttpResponse(stream_response_generator())
def stream_response_generator():
for x in range(1,11):
yield "%s\n" % x # Returns a chunk of the response to the browser
time.sleep(1)
关于 Apache 的说明
我在 Ubuntu 13.04 上测试了以上内容,使用的是 Apache 2.2。默认情况下启用的 Apache 模块 mod_deflate 会缓存你想要流式传输的内容,直到达到一定的块大小,然后它会对内容进行 gzip 压缩并发送到浏览器。这会导致上面的例子无法按预期工作。避免这个问题的一种方法是通过在 Apache 配置中添加以下行来禁用 mod_deflate:
SetEnvIf Request_URI ^/mysite no-gzip=1
关于这个问题的更多讨论可以参考 如何在 apache2 中禁用 mod_deflate? 的问题。
你可以通过使用 条件装饰器 来关闭 ETAG 中间件。这样,你的响应就可以通过 HTTP 流式传输回来了。你可以用像 curl
这样的命令行工具来确认这一点。不过,这可能不足以让你的浏览器实时显示这个响应。为了让浏览器能够实时显示响应,你可以发送一些空格字符,这样可以强制浏览器的缓冲区填满。下面是一个例子:
from django.views.decorators.http import condition
@condition(etag_func=None)
def stream_response(request):
resp = HttpResponse( stream_response_generator(), content_type='text/html')
return resp
def stream_response_generator():
yield "<html><body>\n"
for x in range(1,11):
yield "<div>%s</div>\n" % x
yield " " * 1024 # Encourage browser to render incrementally
time.sleep(1)
yield "</body></html>\n"