خدمة أرشيفات ZIP التي تم إنشاؤها ديناميكيًا في Django

StackOverflow https://stackoverflow.com/questions/67454

  •  09-06-2019
  •  | 
  •  

سؤال

كيف نخدم المستخدمين بأرشيف ZIP تم إنشاؤه ديناميكيًا في جانغو؟

أقوم بإنشاء موقع حيث يمكن للمستخدمين اختيار أي مجموعة من الكتب المتاحة وتنزيلها كأرشيف ZIP.أنا قلق من أن إنشاء مثل هذه الأرشيفات لكل طلب سيؤدي إلى إبطاء الخادم الخاص بي إلى حد الزحف.لقد سمعت أيضًا أن Django ليس لديه حاليًا حل جيد لخدمة الملفات التي تم إنشاؤها ديناميكيًا.

هل كانت مفيدة؟

المحلول

الحل هو كما يلي.

استخدم وحدة بايثون ملف مضغوط لإنشاء أرشيف مضغوط، ولكن كما يحدد الملف StringIO كائن (يتطلب مُنشئ ZipFile كائنًا يشبه الملف).أضف الملفات التي تريد ضغطها.ثم في تطبيق Django الخاص بك، قم بإرجاع محتوى كائن StringIO إليه HttpResponse مع تعيين نوع mime إلى application/x-zip-compressed (أو على الأقل application/octet-stream).إذا كنت تريد، يمكنك تعيين content-disposition header، ولكن لا ينبغي أن يكون هذا مطلوبًا حقًا.

لكن احذر، فإن إنشاء أرشيفات مضغوطة عند كل طلب يعد فكرة سيئة وقد يؤدي ذلك إلى إنهاء خادمك (دون احتساب المهلات إذا كانت الأرشيفات كبيرة).يتمثل النهج من حيث الأداء في تخزين المخرجات التي تم إنشاؤها مؤقتًا في مكان ما في نظام الملفات وإعادة إنشائها فقط في حالة تغيير الملفات المصدر.والفكرة الأفضل هي إعداد المحفوظات مسبقًا (على سبيل المثال.بواسطة cron job) واجعل خادم الويب الخاص بك يخدمهم كالإحصائيات المعتادة.

نصائح أخرى

إليك طريقة عرض 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

تقترح العديد من الإجابات هنا استخدام ملف 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

بالنسبة لـ python3 أستخدم io.ByteIO منذ StringIO يتم إهماله لتحقيق ذلك.نأمل أن يساعد.

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

لا يتعامل Django بشكل مباشر مع إنشاء المحتوى الديناميكي (خاصة الملفات المضغوطة).سيتم تنفيذ هذا العمل بواسطة مكتبة بايثون القياسية.يمكنك إلقاء نظرة على كيفية إنشاء ملف مضغوط ديناميكيًا في بايثون هنا.

إذا كنت قلقًا بشأن تباطؤ الخادم الخاص بك، فيمكنك تخزين الطلبات مؤقتًا إذا كنت تتوقع الحصول على العديد من الطلبات نفسها.يمكنك استخدام جانغو إطار ذاكرة التخزين المؤقت لمساعدتك في ذلك.

بشكل عام، يمكن أن يتطلب ضغط الملفات المضغوطة استخدامًا مكثفًا لوحدة المعالجة المركزية (CPU)، ولكن لا ينبغي أن يكون Django أبطأ من إطار عمل ويب Python آخر.

المكونات وقح:يمكنك استخدام django-zipview لنفس الغرض.

بعد pip install django-zipview:

from zipview.views import BaseZipView

from reviews import Review


class CommentsArchiveView(BaseZipView):
    """Download at once all comments for a review."""

    def get_files(self):
        document_key = self.kwargs.get('document_key')
        reviews = Review.objects \
            .filter(document__document_key=document_key) \
            .exclude(comments__isnull=True)

        return [review.comments.file for review in reviews if review.comments.name]

إستعملت جانغو 2.0 و بايثون 3.6.

import zipfile
import os
from io import BytesIO

def download_zip_file(request):
    filelist = ["path/to/file-11.txt", "path/to/file-22.txt"]

    byte_data = BytesIO()
    zip_file = zipfile.ZipFile(byte_data, "w")

    for file in filelist:
        filename = os.path.basename(os.path.normpath(file))
        zip_file.write(file, filename)
    zip_file.close()

    response = HttpResponse(byte_data.getvalue(), content_type='application/zip')
    response['Content-Disposition'] = 'attachment; filename=files.zip'

    # Print list files in zip_file
    zip_file.printdir()

    return response

تقوم هذه الوحدة بإنشاء وتدفق أرشيف: https://github.com/allanlei/python-zipstream

(لست متصلاً بالتنمية.مجرد التفكير في استخدامه.)

أقترح استخدام نموذج منفصل لتخزين تلك الملفات المضغوطة المؤقتة.يمكنك إنشاء ملف مضغوط سريعًا وحفظه في النموذج باستخدام حقل الملف وإرسال عنوان URL في النهاية إلى المستخدم.

مزايا:

  • تقديم ملفات مضغوطة ثابتة باستخدام آلية وسائط Django (مثل التحميلات المعتادة).
  • القدرة على تنظيف الملفات المضغوطة التي لا معنى لها عن طريق تنفيذ البرنامج النصي cron العادي (والذي يمكنه استخدام حقل التاريخ من نموذج الملف المضغوط).

ألا يمكنك فقط كتابة رابط إلى "خادم مضغوط" أو غير ذلك؟لماذا يجب تقديم أرشيف Zip نفسه من Django؟كل ما هو مطلوب هنا هو برنامج نصي CGI من حقبة التسعينيات لإنشاء ملف مضغوط وإخراجه إلى وضع قياسي، على الأقل بقدر ما أستطيع رؤيته.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top