إنشاء أرشيف البريدي للتنزيل الفوري
سؤال
في تطبيق ويب أعمل عليه، يمكن للمستخدم إنشاء أرشيف مضغوط لمجلد مليء بالملفات. هنا هنا الرمز:
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)
ولكن هذا له تأثير جانبي مقرف من الانتظار الطويل (10+ ثانية) بينما يتم تنزيل أرشيف الرمز البريدي. هل من الممكن تخطي هذا؟ بدلا من حفظ الأرشيف إلى ملف، هل من الممكن إرساله مباشرة إلى المستخدم؟
أعتقد أن Torrentflux يوفر ميزة Excat هذه التي أتحدث عنها. القدرة على الدفع GBS من البيانات وتنزيلها في غضون ثانية.
المحلول
نصائح أخرى
كما يقول ماندريك، يقبل منشئ httpresponse الأشياء القريبة.
لحسن الحظ، فإن تنسيق الرمز البريدي هو مثل هذا الأرشيف يمكن إنشاؤه في تمريرة واحدة، ويقع سجل الدليل المركزي في نهاية الملف:
(صورة من ويكيبيديا)
ولحسن الحظ، zipfile
في الواقع لا يقوم بأي تسعى طالما قمت بإضافة ملفات فقط.
هنا هو الرمز الذي توصلت إليه. بعض الملاحظات:
- أنا أستخدم هذا الرمز لتخفيض مجموعة من الصور JPEG. ليس هناك نقطة ضغط لهم، أنا أستخدم الرمز البريدي فقط كحاوية.
- استخدام الذاكرة هو O (size_of_largest_file) وليس O (size_of_archive). وهذا جيد بما فيه الكفاية بالنسبة لي: العديد من الملفات الصغيرة نسبيا تضيف إلى أرشيف ضخم يحتمل
- لا يقوم هذا الرمز بتعيين رأس طول المحتوى، لذلك لا يحصل المستخدم على إشارة تقدم سعيدة. هو - هي يجب أن يكون ممكن لحساب هذا مقدما إذا كانت أحجام جميع الملفات معروفة.
- تخدم الرمز البريدي مباشرة للمستخدم مثل هذا يعني أن استئناف التنزيلات لن تعمل.
لذلك، هنا يذهب:
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
إليك وظيفة عرض DJANGO بسيطة تصل إلى أي ملفات مقروءة (كمثال) /tmp
وإرجاع ملف zip.
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()
مع file = open('/path/to/yourfiles.zip', 'wb')
واستبدال ذلك file.getvalue()
مع رمز لقراءة محتويات ملف القرص.
هل تتسم مكتبة الرمز البريدي في استخدام الإخراج إلى دفق. يمكنك الدخول مباشرة إلى المستخدم بدلا من الكتابة مؤقتا إلى ملف مضغوط ثم تدفق المستخدم.
من الممكن اجتياز مقاومة للماء في منشئ HTTPRESPONSE (انظر المستندات). وبعد من شأنه أن يسمح لك بإنشاء جهاز كمتدرج مخصص ينشئ بيانات كما يتم طلبه. ومع ذلك، لا أعتقد أن ذلك سيعمل مع الرمز البريدي (سيكون عليك إرسال الرمز البريدي الجزئي لأنه يتم إنشاؤه).
الطريقة الصحيحة، أعتقد أن إنشاء الملفات دون اتصال بالإنترنت، في عملية منفصلة. يمكن للمستخدم بعد ذلك مراقبة التقدم المحرز ثم قم بتنزيل الملف عند استعداده (ربما باستخدام طريقة ITERATOR الموضحة أعلاه). سيكون هذا مماثلة ما هي المواقع مثل استخدام YouTube عند تحميل ملف وانتظر معالجةها.