Django

2008-09-15 6 views
45

में गतिशील रूप से जेनरेट किए गए ज़िप अभिलेखागार की सेवा करना उपयोगकर्ताओं को Django में गतिशील रूप से जेनरेट किए गए ज़िप संग्रह की सेवा कैसे करें?Django

मैं एक साइट बना रहा हूं, जहां उपयोगकर्ता उपलब्ध पुस्तकों का संयोजन चुन सकते हैं और उन्हें ज़िप संग्रह के रूप में डाउनलोड कर सकते हैं। मुझे चिंता है कि प्रत्येक अनुरोध के लिए ऐसे अभिलेखागार उत्पन्न करने से मेरा सर्वर क्रॉल हो जाएगा। मैंने यह भी सुना है कि Django वर्तमान में गतिशील रूप से जेनरेट की गई फ़ाइलों की सेवा के लिए एक अच्छा समाधान नहीं है।

उत्तर

38

समाधान निम्नानुसार है।

ज़िप संग्रह बनाने के लिए पायथन मॉड्यूल zipfile का उपयोग करें, लेकिन फ़ाइल StringIO ऑब्जेक्ट निर्दिष्ट करें (ज़िपफाइल कन्स्ट्रक्टर को फ़ाइल जैसी ऑब्जेक्ट की आवश्यकता है)। उन फ़ाइलों को जोड़ें जिन्हें आप संपीड़ित करना चाहते हैं। फिर आपके Django एप्लिकेशन में HttpResponse में स्ट्रिंगियो ऑब्जेक्ट की सामग्री को application/x-zip-compressed (या कम से कम application/octet-stream) पर सेट करें। यदि आप चाहते हैं, तो आप content-disposition शीर्षलेख सेट कर सकते हैं, लेकिन यह वास्तव में आवश्यक नहीं होना चाहिए।

लेकिन सावधान रहें, प्रत्येक अनुरोध पर ज़िप अभिलेखागार बनाना बुरा विचार है और यह आपके सर्वर को मार सकता है (अभिलेखागार बड़े होने पर टाइमआउट की गणना नहीं कर रहा है)। प्रदर्शन-वार दृष्टिकोण फाइल सिस्टम में कहीं भी उत्पन्न आउटपुट को कैश करना है और केवल तब ही पुन: उत्पन्न करना है जब स्रोत फ़ाइलों को बदल दिया गया हो। यहां तक ​​कि बेहतर विचार है कि अभिलेखागार अग्रिम में तैयार करें (उदाहरण के लिए क्रॉन जॉब द्वारा) और अपना वेब सर्वर सामान्य स्टेटिक्स के रूप में उनकी सेवा कर रहा है।

+0

स्ट्रिंगियो पायथन 3.0 में जाएगा, इसलिए आप तदनुसार अपना कोड ब्रैकेट करना चाहेंगे। –

+11

यह नहीं चला गया है, बस आईओ मॉड्यूल में ले जाया गया। http://docs.python.org/3.0/library/io.html#io.StringIO –

+1

बस एक विचार के रूप में, जैसा कि आप पहले ही मैन्युअल रूप से एक HttpResponse बना रहे हैं, क्या आप इसे बफर के रूप में उपयोग नहीं कर सकते? इसके द्वारा मेरा मतलब है कि 'ज़िपफाइल' की प्रतिक्रिया पास करें और इसे सीधे लिखने दें। मैंने इसे अन्य चीजों के साथ किया है। यदि आप भारी धाराओं से निपट रहे हैं, तो यह तेज़ और अधिक मेमोरी कुशल हो सकता है। – Oli

0

क्या आप सिर्फ "ज़िप सर्वर" या क्या नहीं लिख सकते हैं? ज़िप संग्रह को खुद को Django से क्यों सेवा की आवश्यकता है? एक ज़िप उत्पन्न करने के लिए एक 90 की युग CGI स्क्रिप्ट और इसे stdout पर थूकना वास्तव में यहां तक ​​कि सभी की आवश्यकता है, कम से कम जहां तक ​​मैं देख सकता हूं।

6

Django सीधे गतिशील सामग्री (विशेष रूप से ज़िप फ़ाइलों) की पीढ़ी को संभाल नहीं करता है। वह काम पायथन की मानक पुस्तकालय द्वारा किया जाएगा। आप पाइथन here में गतिशील रूप से ज़िप फ़ाइल बनाने के तरीके को देख सकते हैं।

यदि आप इसके बारे में चिंतित हैं तो अपने सर्वर को धीमा कर देते हैं तो आप अनुरोधों को कैश कर सकते हैं यदि आप एक ही अनुरोध के लिए अपेक्षा करते हैं। आप इसके साथ मदद करने के लिए Django के cache framework का उपयोग कर सकते हैं।

कुल मिलाकर, ज़िप फ़ाइलें सीपीयू गहन हो सकती हैं लेकिन Django किसी अन्य पायथन वेब ढांचे की तुलना में धीमी नहीं होनी चाहिए।

1

मैं उन temp ज़िप फ़ाइलों को संग्रहीत करने के लिए अलग मॉडल का उपयोग करने का सुझाव देता हूं। आप ज़िप पर फ्लाई बना सकते हैं, फाइलफील्ड के साथ मॉडल में सहेज सकते हैं और आखिरकार उपयोगकर्ता को यूआरएल भेज सकते हैं।

लाभ:

  • (सामान्य अपलोड की तरह) Django मीडिया तंत्र के साथ स्थिर ज़िप फ़ाइलों की सेवा।
  • नियमित क्रॉन स्क्रिप्ट निष्पादन (जो ज़िप फ़ाइल मॉडल से दिनांक फ़ील्ड का उपयोग कर सकते हैं) द्वारा बाली ज़िप फ़ाइलों को साफ़ करने की क्षमता।
37

यहाँ यह करने के लिए एक 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 
+2

का समर्थन नहीं करता है इस उदाहरण में आवश्यक नहीं है, लेकिन सामान्य रूप से सुनिश्चित करें कि सामग्री-स्वभाव हेडर में फ़ाइल नाम उद्धृत किया गया है और से बच निकला है जरूरत है। उदाहरण के लिए, यदि फ़ाइल नाम में कोई स्थान है, तो अधिकांश ब्राउज़र केवल फ़ाइल नाम के लिए स्थान तक भाग का उपयोग करेंगे (उदाहरण के लिए 'संलग्नक; फ़ाइल नाम = टेस्ट फ़ाइल। ज़िप' 'टेस्ट' के रूप में सहेजा जाता है।) –

+0

@ माइकडेसिमोन अच्छा बिंदु । इस तरह के संदर्भ के लिए फ़ाइल नाम से बचने के लिए कोई अच्छी विधि है? – dbr

+0

http://stackoverflow.com/questions/93551/how-to-encode-the-filename-parameter-of-content-disposition-header-in-http –

2

इस मॉड्यूल उत्पन्न करता है और एक संग्रह धाराओं: https://github.com/allanlei/python-zipstream

(मैं विकास से जुड़ा नहीं कर रहा हूँ बस इसे का उपयोग कर के बारे में सोच।।)

5

बेकार प्लग: आप उसी उद्देश्य के लिए 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] 
3

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 
+0

इस तरह के कोड का उपयोग कर, मैं फ़ाइल को सही तरीके से नामित नहीं कर सकता। फिलहाल, यह सिर्फ एक यादृच्छिक स्ट्रिंग है जो यूयूआईडी की तरह दिखता है। – freethebees