2009-06-14 21 views
5

在我正在處理的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數據並在一秒之內下載。

回答

2

是否使用的是允許輸出到一個流的zip庫。您可以直接流式傳輸給用戶,而不是暫時寫入壓縮文件,然後流式傳輸給用戶。

+0

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

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 

當然如果zip文件將可以方便地裝入內存這種做法只會工作 - 如果不是,你必須使用磁盤文件(你想避免)。在這種情況下,您只需將file = StringIO()替換爲file = open('/path/to/yourfiles.zip', 'wb')並用代碼替換file.getvalue()以讀取磁盤文件的內容。

0

可以將一個迭代器傳遞給HttpResponse的構造函數(see docs)。這將允許您創建一個自定義迭代器,用於在請求時生成數據。不過,我認爲這不適用於zip(您將不得不在發送時發送部分zip)。

我認爲,正確的方法是在單獨的過程中脫機創建文件。然後,用戶可以監視進度,然後在準備就緒時(可能通過使用上述迭代器方法)下載文件。這與YouTube上的網站在上傳文件並等待它被處理時使用的類似。

8

正如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 
相關問題