创建zip存档以供即时下载

Jos*_*unt 5 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 = settings.PYRAT_DOWNLOAD_BASE_URL + "/" + settings.PYRAT_ARCHIVE_DIR + "/" + filename
output = "Download <a href=\"" + downloadurl + "\">" + torrent_name + "</a>"
return HttpResponse(output)
Run Code Online (Sandbox Code Playgroud)

但是,在下载zip存档时,这会产生长时间等待(10秒以上)的恶劣副作用.有可能跳过这个吗?而不是将存档保存到文件,是否可以直接发送给用户?

我确实相信torrentflux提供了我正在谈论的这个令人兴奋的功能.能够压缩GB数据并在一秒钟内下载.

Pēt*_*une 9

正如Mandrake所说,HttpResponse的构造函数接受可迭代对象.

幸运的是,ZIP格式可以在单个传递中创建存档,中央目录记录位于文件的最末端:

在此输入图像描述

(图片来自维基百科)

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

这是我想出的代码.一些说明:

  • 我正在使用此代码来压缩一堆JPEG图片.压缩它们毫无意义,我只使用ZIP作为容器.
  • 内存使用量为O(size_of_largest_file)而不是O(size_of_archive).这对我来说已经足够了:许多相对较小的文件可以构成潜在的巨大存档
  • 此代码未设置Content-Length标头,因此用户无法获得良好的进度指示.它应该是可能的,如果所有的文件的大小是人所共知的提前计算出这一点.
  • 像这样直接向用户提供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
Run Code Online (Sandbox Code Playgroud)


Vin*_*jip 5

这是一个简单的Django视图函数,它将任何可读文件拉上(作为示例)/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
Run Code Online (Sandbox Code Playgroud)

当然,这种方法只有在zip文件方便地放入内存时才有效 - 如果没有,你必须使用磁盘文件(你试图避免使用).在这种情况下,您只需替换file = StringIO()with file = open('/path/to/yourfiles.zip', 'wb')并替换file.getvalue()with代码即可读取磁盘文件的内容.