在 Django 中提供大内容(或大文件)下载
2011-07-21
Django 框架建站很方便,不过有时也会遇到一些麻烦,比如至少到目前的版本中它都还没有提供 flush 方法,有时需要向客户端输出一个非常大的内容,怎么处理呢?
如果这个非常大的内容是一个文件,最简单的办法就是使用静态文件,让 Apache 等服务器来处理大文件的下载。不过有时,我们可能需要在下载之前先检查一下用户的权限,可能不能向用户暴露文件的真实地址,或者这个大内容是临时生成的(比如临时将多个文件合并而成的),这时就不能使用静态文件了。
在 PHP 甚至 ASP 中,都有 flush 或类似方法,比如 PHP 中可以这样做:
do_something();
print 'Done doing something!';
flush();
do_something_else();
print 'Done doing something else!';
flush();
但 Django 中还没有实现对应的方法。一个解决办法是先将要传送的内容全生成在内存中,然后再一次性传入 HttpResponse,比如:
def bigFileView(request):
# do something...
c = open("big_file.txt", "rb").read()
return HttpResponse(c)
这样处理最简单粗暴,但也存在很大的问题,如果这个文件非常大,这样处理可能会占用大量的内存,甚至导致服务器崩溃。
不过 Django 的开发者应该也考虑到了这种情况,官方文档中提到,可以给 HTTPResponse 传入一个迭代器,同时,StackOverflow 上也有一个讨论这个问题的页面。根据这两个页面的内容,上面这个下载大文件的问题可以这样解决:
def bigFileView(request):
# do something...
def readFile(fn, buf_size=262144):
f = open(fn, "rb")
while True:
c = f.read(buf_size)
if c:
yield c
else:
break
f.close()
file_name = "big_file.txt"
response = HttpResponse(readFile(file_name))
return response
虽然这和其它语言中的 flush 不太一样,并且也并不是每次 yield 时客户端都会收到相应的内容(可能是因为服务器或客户端的缓冲),但这样的方法的确可以解决大文件或大内容下载的问题。经测试,使用这样的方法下载大文件时,服务器的内存占用几乎没有变化。
前一篇学习 newLISP
后一篇BeansDB 学习
发表评论:
电子邮件地址不会被公开。必填项已用 * 标注。
评论: