2009-06-14 55 views
5

Trong ứng dụng web tôi đang làm việc, người dùng có thể tạo lưu trữ zip của một thư mục chứa đầy đủ các tệp. Dưới đây là đoạn mã:Tạo kho lưu trữ zip để tải xuống tức thì

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) 

Nhưng điều này có tác dụng phụ khó chịu khi chờ đợi lâu (10+ giây) trong khi lưu trữ zip đang được tải xuống. Có thể bỏ qua điều này không? Thay vì lưu tệp lưu trữ vào một tệp, bạn có thể gửi tệp trực tiếp tới người dùng không?

Tôi tin rằng torrentflux cung cấp tính năng kích thích này mà tôi đang nói đến. Có thể nén GB dữ liệu và tải xuống trong vòng một giây.

Trả lời

2

Thư viện zip bạn đang sử dụng có cho phép xuất ra luồng không. Bạn có thể truyền trực tiếp tới người dùng thay vì tạm thời ghi vào một tệp zip THEN phát trực tiếp tới người dùng.

+0

Tôi nghĩ rằng đây có thể là những gì anh ta hỏi. – Travis

+0

Nó cho phép các đối tượng giống như tệp. Người ta có thể có một đối tượng giống như tập tin hoạt động như luồng đệm - xem câu trả lời của tôi! –

5

Dưới đây là một chức năng xem Django đơn giản sẽ nén lên (ví dụ như bất kỳ tệp nào có thể đọc được trong /tmp và trả về tệp nén.

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 

Tất nhiên cách tiếp cận này sẽ chỉ hoạt động nếu tệp zip phù hợp với bộ nhớ - nếu không, bạn sẽ phải sử dụng tệp đĩa (mà bạn đang cố tránh). Trong trường hợp đó, bạn chỉ cần thay thế file = StringIO() bằng file = open('/path/to/yourfiles.zip', 'wb') và thay thế file.getvalue() bằng mã để đọc nội dung của tệp đĩa.

0

Có thể chuyển một trình vòng lặp tới hàm tạo của một HttpResponse (see docs). Điều đó sẽ cho phép bạn tạo một trình vòng lặp tùy chỉnh tạo dữ liệu khi nó được yêu cầu. Tuy nhiên tôi không nghĩ rằng sẽ làm việc với một zip (bạn sẽ phải gửi một phần zip như nó đang được tạo ra).

Cách thích hợp, tôi nghĩ, sẽ tạo các tệp ngoại tuyến, trong một quy trình riêng biệt. Sau đó, người dùng có thể theo dõi tiến độ và sau đó tải xuống tệp khi đã sẵn sàng (có thể bằng cách sử dụng phương thức trình lặp được mô tả ở trên). Điều này tương tự với những trang web như YouTube sử dụng khi bạn tải lên tệp và chờ tệp được xử lý.

8

Như mandrake nói, hàm tạo của HttpResponse chấp nhận các đối tượng có thể lặp lại.

May mắn thay, định dạng ZIP là như vậy mà lưu trữ có thể được tạo ra trong lần chạy đơn, ghi thư mục trung tâm nằm ở cuối của file:

enter image description here

(Ảnh từ Wikipedia)

Và may mắn thay, zipfile thực sự không thực hiện bất kỳ tìm kiếm nào miễn là bạn chỉ thêm tệp.

Đây là mã tôi đã đưa ra. Một số lưu ý:

  • Tôi đang sử dụng mã này để nén một loạt ảnh JPEG. Không có điểm nào nén chúng, tôi chỉ sử dụng ZIP làm vùng chứa.
  • Mức sử dụng bộ nhớ là O (size_of_largest_file) không phải O (size_of_archive).Và điều này là đủ tốt đối với tôi: nhiều tệp tương đối nhỏ bổ sung vào kho lưu trữ tiềm năng khổng lồ
  • Mã này không đặt tiêu đề Độ dài nội dung, do đó người dùng không nhận được chỉ báo tiến trình tốt đẹp. Có thể có thể để tính toán trước nếu kích thước của tất cả các tệp được biết.
  • Cung cấp ZIP trực tiếp cho người dùng như vậy có nghĩa là việc tiếp tục tải xuống sẽ không hoạt động.

Vì vậy, ở đây đi:

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 
Các vấn đề liên quan