创建zip存档以便即时下载

问题描述:

在我正在处理的Web应用程序中,用户可以创建一个文件夹的zip存档文件。这里下面的代码:创建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) 

但是,这有一个漫长的等待(10+秒),而zip压缩包正在下载的讨厌的副作用。有没有可能跳过这个?不是将档案保存到文件中,而是可以直接发送给用户?

我确实相信torrentflux提供了我正在谈论的这个激动人心的功能。能够压缩GB数据并在一秒之内下载。

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

+0

我想这可能是他在问。 – Travis 2009-07-09 13:28:36

下面是一个简单的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 

当然如果zip文件将可以方便地装入内存这种做法只会工作 - 如果不是,你必须使用磁盘文件(你想避免)。在这种情况下,您只需将file = StringIO()替换为file = open('/path/to/yourfiles.zip', 'wb')并用代码替换file.getvalue()以读取磁盘文件的内容。

可以将一个迭代器传递给HttpResponse的构造函数(see docs)。这将允许您创建一个自定义迭代器,用于在请求时生成数据。不过,我认为这不适用于zip(您将不得不在发送时发送部分zip)。

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

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

幸运的是,ZIP格式是这样的存档可以在单传,*目录记录位于文件的最后创建:

enter image description here

(图片来自Wikipedia

而且幸运的是,只要您只添加文件,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