5

私が取り組んでいる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)

ただし、これには、zipアーカイブのダウンロード中に長時間(10秒以上)待機するという厄介な副作用があります。これをスキップすることは可能ですか?アーカイブをファイルに保存する代わりに、ユーザーに直接送信することは可能ですか?

私は、torrentfluxが私が話しているこのexcat機能を提供すると信じています。GBのデータを圧縮して1秒以内にダウンロードできること。

4

5 に答える 5

12

Djangoで動的に生成されたZIPアーカイブを提供するこのサービスを確認してください

于 2009-06-14T10:47:16.027 に答える
9

mandrakeが言うように、HttpResponseのコンストラクターは反復可能なオブジェクトを受け入れます。

幸い、ZIP形式では、アーカイブをシングルパスで作成でき、中央ディレクトリレコードはファイルの最後にあります。

ここに画像の説明を入力してください

ウィキペディアからの写真)

そして幸いなことに、zipfileファイルを追加するだけである限り、実際にはシークは実行されません。

これが私が思いついたコードです。いくつかのメモ:

  • このコードを使用して、大量のJPEG画像を圧縮しています。それらを圧縮する意味はありません。私はZIPをコンテナとしてのみ使用しています。
  • メモリ使用量はO(size_of_archive)ではなくO(size_of_largest_file)です。そして、これは私にとっては十分です:潜在的に巨大なアーカイブに追加される多くの比較的小さなファイル
  • このコードは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
于 2012-03-22T19:19:18.747 に答える
5

これは、(例として)読み取り可能なファイルを圧縮し/tmpてzipファイルを返す単純なDjangoビュー関数です。

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()withfile = open('/path/to/yourfiles.zip', 'wb')を置き換え、file.getvalue()withコードを置き換えるだけで、ディスクファイルの内容を読み取ることができます。

于 2009-07-03T20:16:18.627 に答える
2

使用しているzipライブラリはストリームへの出力を許可していますか?一時的にzipファイルに書き込んでからユーザーにストリーミングする代わりに、ユーザーに直接ストリーミングすることができます。

于 2009-06-14T10:57:08.547 に答える
0

HttpResponseのコンストラクターにイテレーターを渡すことができます(ドキュメントを参照)。これにより、要求されたときにデータを生成するカスタムイテレータを作成できます。ただし、これはzipでは機能しないと思います(作成時に部分的なzipを送信する必要があります)。

適切な方法は、別のプロセスでファイルをオフラインで作成することだと思います。その後、ユーザーは進行状況を監視し、準備ができたらファイルをダウンロードできます(おそらく、上記のイテレーター方式を使用して)。これは、ファイルをアップロードして処理されるのを待つときにyoutubeなどのサイトが使用するものと似ています。

于 2009-07-09T12:19:50.837 に答える