67

Djangoで動的に生成されたZIPアーカイブをユーザーに提供するには?

ユーザーが利用可能な書籍の任意の組み合わせを選択し、それらを ZIP アーカイブとしてダウンロードできるサイトを作成しています。リクエストごとにこのようなアーカイブを生成すると、サーバーの速度が低下してクロールするのではないかと心配しています。また、現在 Django には、動的に生成されたファイルを提供するための適切なソリューションがないと聞いています。

4

10 に答える 10

49

解決策は次のとおりです。

Python モジュールzipfileを使用して zip アーカイブを作成しますが、ファイルとしてStringIOオブジェクトを指定します (ZipFile コンストラクターにはファイルのようなオブジェクトが必要です)。圧縮したいファイルを追加します。次に、Django アプリケーションでHttpResponse、mimetype をapplication/x-zip-compressed(または少なくともapplication/octet-stream) に設定して StringIO オブジェクトのコンテンツを返します。必要に応じてヘッダーを設定できますがcontent-disposition、これは実際には必須ではありません。

ただし、リクエストごとに zip アーカイブを作成することはお勧めできません。これにより、サーバーが停止する可能性があります (アーカイブが大きい場合、タイムアウトはカウントされません)。パフォーマンス上のアプローチは、生成された出力をファイルシステムのどこかにキャッシュし、ソース ファイルが変更された場合にのみ再生成することです。さらに良いアイデアは、事前にアーカイブを準備し (例: cron ジョブによって)、Web サーバーがそれらを通常の静的として提供するようにすることです。

于 2008-09-16T13:30:56.950 に答える
46

これを行う Django ビューを次に示します。

import os
import zipfile
import StringIO

from django.http import HttpResponse


def getfiles(request):
    # Files (local path) to put in the .zip
    # FIXME: Change this (get paths from DB etc)
    filenames = ["/tmp/file1.txt", "/tmp/file2.txt"]

    # Folder name in ZIP archive which contains the above files
    # E.g [thearchive.zip]/somefiles/file2.txt
    # FIXME: Set this to something better
    zip_subdir = "somefiles"
    zip_filename = "%s.zip" % zip_subdir

    # Open StringIO to grab in-memory ZIP contents
    s = StringIO.StringIO()

    # The zip compressor
    zf = zipfile.ZipFile(s, "w")

    for fpath in filenames:
        # Calculate path for file in zip
        fdir, fname = os.path.split(fpath)
        zip_path = os.path.join(zip_subdir, fname)

        # Add file, at correct path
        zf.write(fpath, zip_path)

    # Must close zip for all contents to be written
    zf.close()

    # Grab ZIP file from in-memory, make response with correct MIME-type
    resp = HttpResponse(s.getvalue(), mimetype = "application/x-zip-compressed")
    # ..and correct content-disposition
    resp['Content-Disposition'] = 'attachment; filename=%s' % zip_filename

    return resp
于 2012-10-18T09:32:10.023 に答える
30

StringIOここでの多くの回答は、またはBytesIOバッファを使用することを提案しています。ただし、HttpResponseすでにファイルのようなオブジェクトであるため、これは必要ありません。

response = HttpResponse(content_type='application/zip')
zip_file = zipfile.ZipFile(response, 'w')
for filename in filenames:
    zip_file.write(filename)
response['Content-Disposition'] = 'attachment; filename={}'.format(zipfile_name)
return response
于 2018-03-11T12:32:41.990 に答える
8

Python3 の場合、これを実現するために StringIO が非推奨になっているため、io.ByteIO使用します。それが役に立てば幸い。

import io

def my_downloadable_zip(request):
    zip_io = io.BytesIO()
    with zipfile.ZipFile(zip_io, mode='w', compression=zipfile.ZIP_DEFLATED) as backup_zip:
        backup_zip.write('file_name_loc_to_zip') # u can also make use of list of filename location
                                                 # and do some iteration over it
     response = HttpResponse(zip_io.getvalue(), content_type='application/x-zip-compressed')
     response['Content-Disposition'] = 'attachment; filename=%s' % 'your_zipfilename' + ".zip"
     response['Content-Length'] = zip_io.tell()
     return response
于 2016-10-17T00:10:09.437 に答える
6

Django は、動的コンテンツ (特に Zip ファイル) の生成を直接処理しません。その作業は、Python の標準ライブラリによって行われます。Python で Zip ファイルを動的に作成する方法については、こちらを参照してください。

サーバーの速度が低下することが心配な場合は、同じリクエストが多数あると予想される場合は、リクエストをキャッシュできます。これには、Django のキャッシュ フレームワークを使用できます。

全体として、ファイルの圧縮は CPU を集中的に使用する可能性がありますが、Django は他の Python Web フレームワークよりも遅くはないはずです。

于 2008-09-15T22:09:50.130 に答える
1

これらの一時 zip ファイルを保存するには、別のモデルを使用することをお勧めします。zip をその場で作成し、ファイル フィールドを使用してモデルに保存し、最後に URL をユーザーに送信できます。

利点:

  • django メディア メカニズムを使用して静的 zip ファイルを提供します (通常のアップロードと同様)。
  • 定期的な cron スクリプトの実行によって古い zip ファイルをクリーンアップする機能 (zip ファイル モデルの日付フィールドを使用できます)。
于 2008-09-16T15:31:39.267 に答える
-1

「zipサーバー」などへのリンクを書くことはできませんか? zip アーカイブ自体を Django から提供する必要があるのはなぜですか? zip を生成して stdout に吐き出す 90 年代の CGI スクリプトは、少なくとも私が見る限り、ここで必要なすべてです。

于 2008-09-15T22:03:03.593 に答える