如何在Django中爲用戶提供動態生成的ZIP壓縮文件?在Django中爲動態生成的ZIP壓縮文件提供服務
我正在製作一個網站,用戶可以選擇任何可用書籍的組合並將它們下載爲ZIP存檔。我擔心爲每個請求生成這樣的檔案會使我的服務器變慢。我也聽說Django目前沒有一個好的解決方案來提供動態生成的文件。
如何在Django中爲用戶提供動態生成的ZIP壓縮文件?在Django中爲動態生成的ZIP壓縮文件提供服務
我正在製作一個網站,用戶可以選擇任何可用書籍的組合並將它們下載爲ZIP存檔。我擔心爲每個請求生成這樣的檔案會使我的服務器變慢。我也聽說Django目前沒有一個好的解決方案來提供動態生成的文件。
解決方案如下。
使用Python模塊zipfile創建zip存檔,但是作爲文件指定StringIO對象(ZipFile構造函數需要類文件對象)。添加要壓縮的文件。然後在Django應用程序中返回HttpResponse
中的StringIO對象的內容,其中mimetype設置爲application/x-zip-compressed
(或至少application/octet-stream
)。如果你願意,你可以設置content-disposition
標題,但這不應該是真正需要的。
但要小心,每個請求創建zip檔案是個壞主意,這可能會導致你的服務器中斷(如果檔案很大,則不計算超時)。性能優化的方法是將生成的輸出緩存到文件系統的某個位置,並且只有在源文件發生更改時才重新生成輸出。更好的辦法是提前準備檔案(例如通過cron作業),讓你的網絡服務器像往常一樣服務於他們。
難道你不能只寫一個鏈接到「郵編服務器」或什麼?爲什麼zip檔案本身需要從Django提供?一個90年代的CGI腳本生成一個zip並將其吐出到標準輸出中是至關重要的,至少據我所知。
Django不直接處理動態內容(特別是Zip文件)的生成。這項工作將由Python的標準庫完成。你可以看看如何在Python here中動態創建一個Zip文件。
如果您擔心會減慢服務器速度,那麼您可以緩存請求,如果您希望有很多相同的請求。你可以使用Django的cache framework來幫助你。
總體而言,壓縮文件可能是CPU密集型的,但Django不應該比其他Python Web框架慢。
我建議使用單獨的模型來存儲這些臨時壓縮文件。您可以創建zip文件,保存到filefield模型中,最後將url發送給用戶。
優點:
這裏的Django視圖來做到這一點:
import os
import zipfile
import StringIO
from django.http import HttpResponse
def getfiles(request):
# Files (local path) to put in the .zip
# FIXME: Change this (get paths from DB etc)
filenames = ["/tmp/file1.txt", "/tmp/file2.txt"]
# Folder name in ZIP archive which contains the above files
# E.g [thearchive.zip]/somefiles/file2.txt
# FIXME: Set this to something better
zip_subdir = "somefiles"
zip_filename = "%s.zip" % zip_subdir
# Open StringIO to grab in-memory ZIP contents
s = StringIO.StringIO()
# The zip compressor
zf = zipfile.ZipFile(s, "w")
for fpath in filenames:
# Calculate path for file in zip
fdir, fname = os.path.split(fpath)
zip_path = os.path.join(zip_subdir, fname)
# Add file, at correct path
zf.write(fpath, zip_path)
# Must close zip for all contents to be written
zf.close()
# Grab ZIP file from in-memory, make response with correct MIME-type
resp = HttpResponse(s.getvalue(), mimetype = "application/x-zip-compressed")
# ..and correct content-disposition
resp['Content-Disposition'] = 'attachment; filename=%s' % zip_filename
return resp
此模塊生成並流檔案:https://github.com/allanlei/python-zipstream
(我不連接到開發只是想使用它。 )
無恥插頭:您可以使用django-zipview用於相同的目的。
後pip install django-zipview
:
from zipview.views import BaseZipView
from reviews import Review
class CommentsArchiveView(BaseZipView):
"""Download at once all comments for a review."""
def get_files(self):
document_key = self.kwargs.get('document_key')
reviews = Review.objects \
.filter(document__document_key=document_key) \
.exclude(comments__isnull=True)
return [review.comments.file for review in reviews if review.comments.name]
對於python3我使用io.ByteIO因爲StringIO的已被棄用,以實現這一目標。希望能幫助到你。
import io
def my_downloadable_zip(request):
zip_io = io.BytesIO()
with zipfile.ZipFile(zip_io, mode='w', compression=zipfile.ZIP_DEFLATED) as backup_zip:
backup_zip.write('file_name_loc_to_zip') # u can also make use of list of filename location
# and do some iteration over it
response = HttpResponse(zip_io.getvalue(), content_type='application/x-zip-compressed')
response['Content-Disposition'] = 'attachment; filename=%s' % 'your_zipfilename' + ".zip"
response['Content-Length'] = zip_io.tell()
return response
StringIO將在Python 3.0中消失,因此您可能需要相應地包圍您的代碼。 – 2009-01-07 19:27:40
它不走了,剛剛搬到這個IO模塊。 http://docs.python.org/3.0/library/io.html#io.StringIO – 2009-06-14 15:52:05
就像一個想法,因爲你已經手動創建一個HttpResponse對象,你能不能把它作爲緩衝?我的意思是將響應傳遞給`zipfile`並讓它直接寫入。我用其他的東西做了。如果你正在處理大量的流,它可能會更快,更高效地存儲內存。 – Oli 2012-01-17 18:34:26