在Django中dynamic生成ZIP压缩文件
如何在Django中为用户提供dynamic生成的ZIP压缩文件?
我正在创build一个网站,用户可以select任何可用书籍的组合,并将其下载为ZIP存档。 我担心为每个请求生成这样的档案会减慢我的服务器爬行。 我也听说Django目前没有一个好的解决scheme来提供dynamic生成的文件。
解决scheme如下。
使用Python模块zipfile来创buildzip归档文件,但是当文件指定StringIO对象(ZipFile构造函数需要类文件对象)时。 添加要压缩的文件。 然后在Django应用程序中返回HttpResponse
的StringIO对象的内容,并将mimetype设置为application/x-zip-compressed
(或者至lessapplication/octet-stream
)。 如果你想,你可以设置content-disposition
标题,但这不应该是真正需要的。
但要小心,每个请求创buildzip档案是不好的主意,这可能会杀了你的服务器(如果档案很大,不计算超时)。 性能方面的方法是将生成的输出caching到文件系统中的某个位置,并且只有在源文件已经改变时才重新生成输出。 更好的办法是提前准备档案(例如通过cron作业),让你的networking服务器像往常一样服务于他们。
这是一个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 # Eg [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
Django不直接处理dynamic内容(特别是Zip文件)的生成。 这项工作将由Python的标准库完成。 你可以看看如何在Python中dynamic创build一个Zip文件。
如果您担心会降低您的服务器速度,那么您可以caching这些请求,如果您希望有很多相同的请求。 你可以使用Django的caching框架来帮助你。
总的来说,压缩文件可能是CPU密集型的,但是Django不应该比其他Python Web框架慢。
无耻的插件:你可以使用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]
该模块生成并stream式存档: https : //github.com/allanlei/python-zipstream
(我没有联系到开发,只是想着使用它。)
对于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
我build议使用单独的模型来存储这些临时压缩文件。 您可以创buildzip,保存到filefield模型,最后发送url给用户。
优点:
- 使用django媒体机制提供静态zip文件(如通常的上传)。
- 能够通过执行正常的cron脚本(可以使用zip文件模型中的date字段)来清除过时的zip文件。
你不能只写一个“邮编服务器”或什么的链接? 为什么zip档案本身需要从Django提供? 一个90年代的CGI脚本生成一个zip文件并将其吐出到标准输出文件中,这是所有这些所需要的,至less据我所知。