你的Web应用可能会提供下载文件的功能。下载个几百KB乃至几个MB的文件直接用open读取文件返回response就能够了。内存它也不在意你占用它那么点空间。可是,若是下载个几百M乃至几个G的文件直接用open读取文件返回response那就悲剧了,内存可不乐意一会儿被你占用这么多空间。git
解决方法网上相关文章不少,好比:http://djangosnippets.org/snippets/365/github
简单说就是用FileWrapper类来迭代器化一下文件对象,实例化出一个通过更适合大文件下载场景的文件对象,具体实现能够看源代码:django/core/servers/basehttp.py中的FileWrapper类的实现。django
实现原理至关与把内容一点点从文件中读取,放到内存,下载下来,直到完成整个下载过程。这样内存就不会担忧你一会儿占用它那么多空间了。app
也许你有又会碰到一个新的问题,下载下来的文件居然是个空文件。spa
若是你单步跟踪调试会发现文件内容返回的response在经过各个中间件过程当中会被提早使用,最后发现是gzip这个中间件的一段代码对response取了下len,致使提早使用了这个迭代器化的文件对象,从而response返回内容没有了,表现为下载了一个空文件。调试
这个时候你有不少选择:code
看了下Django源码,会发现重写gzip中间件会比较靠谱,只要简单修改一段代码逻辑便可。orm
但要有效,必须在下载的views代码的response返回值中设置个header。server
其实更治本的办法应该是重写FileWapper,由于保证不了其它中间件或其它组建进行了相似gzip中间件的处理。中间件
Django官网也有相关讨论,里面还有各类思路:https://code.djangoproject.com/ticket/2131
问题发现和处理使用的Django版本是1.3.0,也许将来Django应该会提供更好的官方解决方案吧。
下载的views代码片断以下:
def tarball(request, release):
file_name = 'dj-download-%s.tar.gz' % release
file_path = os.path.join(FILE_FOLDER, file_name)
try:
tarball_file = open(file_path)
except IOError:
raise Http404
wrapper = FileWrapper(tarball_file)
response = HttpResponse(wrapper, content_type='application/zip')
response['Content-Encoding'] = 'utf-8' # 设置该值gzip中间件就会直接返回而不进行后续操做
response['Content-Disposition'] = 'attachment; filename=%s' % file_name
return response
修改Django的gzip中间件代码片断以下:
def process_response(self, request, response):
# Avoid gzipping if we've already got a content-encoding.
if response.has_header('Content-Encoding'):
return response# It's not worth compressing non-OK or really short responses.
if response.status_code != 200:
return response
if len(response.content) < 200:
return responsepatch_vary_headers(response, ('Accept-Encoding',))
# ... 省略的代码
相应对比Django的gzip中间件代码片断以下:
def process_response(self, request, response):
# It's not worth compressing non-OK or really short responses.
if response.status_code != 200 or len(response.content) < 200:
return responsepatch_vary_headers(response, ('Accept-Encoding',))
# Avoid gzipping if we've already got a content-encoding.
if response.has_header('Content-Encoding'):
return response
# ... 省略的代码