سؤال

لقد كنت أحاول تحديد طريقة "أفضل الممارسات" لإدارة عمليات تحميل الملفات باستخدام Turbogears 2 ، ولم أجد أي أمثلة حتى الآن. لقد اكتشفت طريقة لتحميل الملف فعليًا ، لكنني لست متأكدًا من مدى موثوقيته لنا.

أيضا ، ما هي طريقة جيدة للحصول على اسم الملفات المحملة؟

    file = request.POST['file']
    permanent_file = open(os.path.join(asset_dirname,
        file.filename.lstrip(os.sep)), 'w')
    shutil.copyfileobj(file.file, permanent_file)
    file.file.close()
    this_file = self.request.params["file"].filename 
    permanent_file.close()

إذا افترضنا أنني أفهم بشكل صحيح ، هل سيتجنب شيء من هذا القبيل مشكلة "التسمية" الأساسية؟ id = uuid.

    file = request.POST['file']
    permanent_file = open(os.path.join(asset_dirname,
        id.lstrip(os.sep)), 'w')
    shutil.copyfileobj(file.file, permanent_file)
    file.file.close()
    this_file = file.filename
    permanent_file.close()
هل كانت مفيدة؟

المحلول

mhawke - أنت على صواب ، يجب عليك التعامل مع ذلك - يعتمد على ما تفعله مع الملف ، إذا لم يكن الأمر مهمًا إذا كان هناك تصادم اسم على سبيل المثال ، فأنت تهتم فقط بأحدث إصدار من بعض البيانات ، فمن المحتمل ألا يكون هناك لا المشكلة ، أو إذا لم يكن اسم الملف مهمًا في الواقع فقط محتويات الملف ، ولكنه لا يزال ممارسته السيئة.

يمكنك استخدام tempfile المسماة في TMP Dir ، ثم نقل الملف بمجرد التحقق من صحة إلى موقعه النهائي. أو يمكنك التحقق من اسم الملف غير موجود بالفعل مثل:

file.name = slugify(myfile.filename)
name, ext = os.path.splitext(file.name)
while os.path.exists(os.path.join(permanent_store, file.name)):
    name += '_'
    file.name = name + ext

raw_file = os.path.join(permanent_store, file.name)

سيتم استخدام طريقة Slugify لترتيب اسم الملف ...

نصائح أخرى

أريد فقط أي شخص يأتي إلى هنا يبحث عن إجابات ، لمعرفة ذلك Allesandro Molinaمكتبة رائعة مستودع يشكل أفضل إجابة على هذا السؤال.

إنه يحل كل من مشكلات التسمية والنسخ ، وسوف يتضمن بشكل جيد في تطبيق Turbogears الخاص بك. يمكنك استخدامه مع MongoDB Gridfs ، كما في هذا المثال:

from depot.manager import DepotManager

# Configure a *default* depot to store files on MongoDB GridFS
DepotManager.configure('default', {
    'depot.backend': 'depot.io.gridfs.GridFSStorage',
    'depot.mongouri': 'mongodb://localhost/db'
})

depot = DepotManager.get()

# Save the file and get the fileid
fileid = depot.create(open('/tmp/file.png'))

# Get the file back
stored_file = depot.get(fileid)
print stored_file.filename
print stored_file.content_type

أو يمكنك بسهولة إنشاء حقول مرفقات في Sqlalchemy النماذج ، مثل:

from depot.fields.sqlalchemy import UploadedFileField

class Document(Base):
    __tablename__ = 'document'

    uid = Column(Integer, autoincrement=True, primary_key=True)
    name = Column(Unicode(16), unique=True)

    content = Column(UploadedFileField)

... وبعد ذلك ، يصبح تخزين المستندات مع الملفات المرفقة (يمكن أن يكون المصدر ملفًا أو بايت) سهلاً مثل:

doc = Document(name=u'Foo', content=open('/tmp/document.xls'))
DBSession.add(doc)

مستودع يدعم كليهما LocalFileStorage, mongodbGridFSStorage, وأمازون S3Storage. وعلى الأقل بالنسبة للملفات المخزنة محليًا وفي S3 ، fileid سيتم توليدها بواسطة uuid.uuid1().

لا أعرف الكثير عن Turbogears وما إذا كان يمكن أن يوفر أي شيء لتجنب ما يلي ، لكن يبدو لي أن هذا الرمز محفوف بالخطر. قد يكون من الممكن للمستخدم الضار الكتابة فوق (أو إنشاء) أي ملف تربطه عملية Python Turbogears.

ماذا إذا asset_dirname هو /tmp, ، محتويات file.filename هو ../../../../../../../etc/passwd ومحتويات الملف root::0:0:root:/root:/bin/bash؟ في بيئة UNIX ، سيفتح هذا الرمز (الأذونات المعلقة) الملف /tmp/../../../../../../../etc/passwd في الوضع المقطوع ، ثم نسخ محتويات الملف الذي تم تحميله إليه - الكتابة فوق ملف كلمة مرور النظام بشكل فعال وتحديد مستخدم جذر بدون كلمة مرور. من المفترض أن هناك أشياء سيئة يمكن القيام بها لآلة Windows أيضًا.

حسنًا ، هذا مثال متطرف يتطلب أن يعمل Python كما root (لا أحد يفعل ذلك ، أليس كذلك؟). حتى إذا كان Python يعمل كمستخدم منخفض الانتشار ، فقد يتم كتابة الملفات التي تم تحميلها مسبقًا في الإرادة.

لتلخيص ، لا تثق في إدخال المستخدم ، وفي هذه الحالة ، قدم المستخدم اسم الملف المتاح في file.filename.

أليس Turbogears فقط أبراج مع إضافات؟ يمكنك التحقق من المساعدة هناك:

http://wiki.pylonshq.com/display/pylonsdocs/form+handling#file-uploads

ومع ذلك ، لا يزال هذا يحتوي على عيب أمني محتمل الذي ذكره Mhawke:

os.path.join(permanent_store, myfile.filename.lstrip(os.sep))

كما هو مذكور أعلاه حقًا إذا كان اسم الملف بطريقة ما ../../../../../etc/passwd ثم يمكنك استبدال هذا الملف ...

لذلك يمكنك فقط الحصول على اسم الملف الفعلي مثل:

os.path.join(permanent_store, myfile.filename.split(os.sep).pop())

لدى Werkzeug وظيفة مساعدة جيدة جدًا لتأمين أسماء الملفات المسمى Secure_filename. أعتقد أنه يمكنك تبنيه واستخدامه.

حول كيفية الذهاب ، سأقوم بإثارة الإجابات الجيدة التي تم تقديمها بالفعل.

إليكم 2 بنس الخاص بي حول تسمية الملف المخزن.

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

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

إنها مسألة تصميم نظام جيد للنظام :).

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