你的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
# ... 省略的代碼