Python 创建即时下载的zip存档

Python 创建即时下载的zip存档,python,django,zip,archive,Python,Django,Zip,Archive,在我正在开发的web应用程序中,用户可以创建一个装满文件的文件夹的zip存档。代码如下: files = torrent[0].files zipfile = z.ZipFile(zipname, 'w') output = "" for f in files: zipfile.write(settings.PYRAT_TRANSMISSION_DOWNLOAD_DIR + "/" + f.name, f.name) downloadurl = set

在我正在开发的web应用程序中,用户可以创建一个装满文件的文件夹的zip存档。代码如下:

files = torrent[0].files
    zipfile = z.ZipFile(zipname, 'w')
    output = ""

    for f in files:
        zipfile.write(settings.PYRAT_TRANSMISSION_DOWNLOAD_DIR + "/" + f.name, f.name)

downloadurl = settings.PYRAT_DOWNLOAD_BASE_URL + "/" + settings.PYRAT_ARCHIVE_DIR + "/" + filename
output = "Download <a href=\"" + downloadurl + "\">" + torrent_name + "</a>"
return HttpResponse(output)
files=torrent[0]。文件
zipfile=z.zipfile(zipname,'w')
output=“”
对于文件中的f:
zipfile.write(settings.PYRAT\u TRANSMISSION\u DOWNLOAD\u DIR+“/”+f.name,f.name)
downloadurl=settings.PYRAT\u DOWNLOAD\u BASE\u URL+“/”+settings.PYRAT\u ARCHIVE\u DIR+“/”+文件名
output=“下载”
返回HttpResponse(输出)
但这会产生一个令人讨厌的副作用,即在下载zip存档文件时需要等待很长时间(10秒以上)。可以跳过这个吗?是否可以直接将归档文件发送给用户,而不是将其保存到文件中


我相信torrentflux提供了我所说的excat功能。能够压缩GBs的数据并在一秒钟内下载。

检查此项

您使用的压缩库是否允许输出到流。您可以直接向用户流式传输,而不是临时写入zip文件,然后再流式传输给用户。

这里有一个简单的Django view函数,它可以将
/tmp
中的任何可读文件(例如)压缩并返回zip文件

from django.http import HttpResponse
import zipfile
import os
from cStringIO import StringIO # caveats for Python 3.0 apply

def somezip(request):
    file = StringIO()
    zf = zipfile.ZipFile(file, mode='w', compression=zipfile.ZIP_DEFLATED)
    for fn in os.listdir("/tmp"):
        path = os.path.join("/tmp", fn)
        if os.path.isfile(path):
            try:
                zf.write(path)
            except IOError:
                pass
    zf.close()
    response = HttpResponse(file.getvalue(), mimetype="application/zip")
    response['Content-Disposition'] = 'attachment; filename=yourfiles.zip'
    return response

当然,只有当zip文件能够方便地放入内存时,这种方法才会起作用——否则,您将不得不使用磁盘文件(您正试图避免使用磁盘文件)。在这种情况下,只需将
file=StringIO()
替换为
file=open('/path/to/yourfiles.zip',wb')
,并将
file.getvalue()
替换为读取磁盘文件内容的代码。

可以将迭代器传递给HttpResponse的构造函数。这将允许您创建一个自定义迭代器,根据请求生成数据。然而,我不认为这将与zip一起工作(您必须在创建部分zip时发送它)


我认为,正确的方法是在单独的过程中离线创建文件。然后,用户可以监视进度,然后在文件准备就绪时下载文件(可能使用上述迭代器方法)。这类似于youtube等网站在上传文件并等待处理时所使用的方式。

正如mandrake所说,HttpResponse的构造函数接受iterable对象

幸运的是,ZIP格式可以一次性创建归档文件,中央目录记录位于文件的末尾:

(图片来源)

幸运的是,
zipfile
确实不会进行任何搜索,只要您只添加文件即可

这是我想出的代码。一些注意事项:

  • 我用这个代码来压缩一堆JPEG图片。压缩它们是没有意义的,我只使用ZIP作为容器
  • 内存使用是O(最大文件的大小),而不是O(归档文件的大小)。这对我来说已经足够好了:许多相对较小的文件加起来就可能成为巨大的归档文件
  • 这段代码并没有设置内容长度标题,所以用户并没有得到很好的进度指示。如果已知所有文件的大小,应该可以提前计算
  • 像这样直接向用户提供ZIP意味着下载时的恢复将不起作用
下面是:

import zipfile

class ZipBuffer(object):
    """ A file-like object for zipfile.ZipFile to write into. """

    def __init__(self):
        self.data = []
        self.pos = 0

    def write(self, data):
        self.data.append(data)
        self.pos += len(data)

    def tell(self):
        # zipfile calls this so we need it
        return self.pos

    def flush(self):
        # zipfile calls this so we need it
        pass

    def get_and_clear(self):
        result = self.data
        self.data = []
        return result

def generate_zipped_stream():
    sink = ZipBuffer()
    archive = zipfile.ZipFile(sink, "w")
    for filename in ["file1.txt", "file2.txt"]:
        archive.writestr(filename, "contents of file here")
        for chunk in sink.get_and_clear():
            yield chunk

    archive.close()
    # close() generates some more data, so we yield that too
    for chunk in sink.get_and_clear():
        yield chunk

def my_django_view(request):
    response = HttpResponse(generate_zipped_stream(), mimetype="application/zip")
    response['Content-Disposition'] = 'attachment; filename=archive.zip'
    return response

我想这可能是他所要求的。它允许类似文件的对象。可以有一个类似文件的对象作为缓冲流——看我的答案!