Requests: إنشاء مشاركات متعددة الأجزاء بدون ملف

تم إنشاؤها على ٣ يناير ٢٠١٣  ·  36تعليقات  ·  مصدر: psf/requests

حاليًا ، الطريقة الوحيدة للحصول على طلب نموذج متعدد الأجزاء هي r = requests.post(url, data=payload, files=files)
والتي قد تحتوي على مكون

Content-Disposition: form-data; name="file"; filename="filename.txt"
Content-Type: text/plain

content
--3eeaadbfda0441b8be821bbed2962e4d--

ومع ذلك ، فقد واجهت حالات يُطلب فيها أن تكون المنشورات بتنسيق متعدد الأجزاء بدون ملف مرتبط ، مثل:

Content-Disposition: form-data; name="key1"

value1
--3eeaadbfda0441b8be821bbed2962e4d

ولكن من المستحيل توليد هذا الأخير بدون الأول.

ربما يمكننا إضافة علامة مثل r = requests.post(url, data=payload, multipart=True) تفرض على المنشور أن يكون متعدد الأجزاء ، حتى بدون ملف.

يسعدني العمل على تنفيذ ذلك إذا بدت فكرة جيدة.

ال 36 كومينتر

وقد سبق بيان ذلك من قبل. سيمثل تغييرًا كبيرًا في واجهة برمجة التطبيقات التي لست متأكدًا من رغبتها في @ kennethreitz .

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

ربما سيكون @ kennethreitz أكثر قابلية للحل الثاني. لا أعتقد أنه يتناسب مع فلسفة تصميم الطلبات أيضًا وسيكون غريبًا بشكل غير عادي نظرًا لبقية واجهة برمجة التطبيقات ، ولكن _shrug_ من يدري.

في الواقع ، لا تدعم واجهة برمجة التطبيقات الحالية إرسال بيانات متعددة الأجزاء بخلاف الملفات وهذا أمر سيء. راجع العدد رقم 935 و shazow / urllib3 / issue / 120

ربما أفتقد شيئًا ما ، لكن التغييرات تبدو بسيطة إلى حد ما بالنسبة لي. لقد قمت بتشكيل المستودع ولدي تغيير مقترح في إصداري هنا: https://github.com/spacecase/requests/commit/45b0b3ce1e76b241b323570a5fc88ae2089c3c3d
(إذا كانت هناك طريقة أفضل للقيام بذلك ، فيرجى إبلاغي بذلك؟ أنا جديد إلى حد ما على جيثب).

قد يحتاج إلى بعض الاختبارات غير الضرورية وسيحتاج إلى تغيير في docstring في api.py ، لكن هل شيء من هذا القبيل معقول؟

لا تكمن المشكلة في التغيير في أنه معقد للتنفيذ ، بل هو اختلاف في واجهة برمجة التطبيقات. أنا من النوع الذي أتعامل معه في هذه المناقشة المستمرة: أعتقد أنه سيكون من المفيد على الأرجح أن يكون لديك طريقة جيدة لتحميل بيانات النماذج متعددة الأجزاء ، لكنني أعتقد أيضًا أن واجهة برمجة التطبيقات الحالية files API جيدة جدًا . لا أعتقد أن إضافة multipart إلى Request API هي الطريقة المثلى.

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

لماذا ا؟ إذا قبلنا طلب الميزة هذا ، فمن المرجح أن يشتكي شخص ما من عدم وجود معلمة json. وأنا متأكد من أن الأشخاص الآخرين يمكن أن يتوصلوا إلى المزيد من المعايير التي يرغبون في إضافتها. كما هو الحال الآن ، تقوم واجهة برمجة التطبيقات بما يجب أن تفعله تمامًا ولا تحتوي على أي kruft قليلًا أو معدومًا. طريقة واحدة للنظر إلى هذا هو KISS. هذا يجعل الطلبات وإرجاع كائنًا رائعًا يجعل استخدام الاستجابة أمرًا سهلاً وطبيعيًا. يفعل ما هو معلن وأنت تفعل الباقي. يعلن عن ترميز متعدد الأجزاء ويقوم بذلك من خلال تصميمه الموثق. قد تبدو محرجة ولكنها موثقة وتعمل.

_ (...) سيشتكي شخص ما من عدم وجود معامل json_

لن يكون هناك سبب لمثل هذه الشكوى لأن json هو نوع فرعي من نوع الوسائط application ( rfc4627 ) وليس multipart نوع الوسائط ( httpbis المسودة 21 )

_قد يبدو محرجا (...) _

إنه أمر محرج بينما لا ينبغي أن يكون كذلك.

بعد قراءة التعليقات السابقة ، أود أن أكرر موقفي: multipart/form-data هو أكثر أنواع MIME شيوعًا المستخدمة حاليًا (بحاجة لمصدر: :)) وعدم دعمه يعد إغفالًا إجماليًا.

@ piotr-dobrogost: على الرغم مما قلته أعلاه ، لا أجد الحجة التي قدمتها للتو مقنعة قليلاً.

الطلبات لا تدعم أنواع MIME ، فهي تدعم حالات الاستخدام. وجهة النظر هذه تجعل كلا من تعليقاتك أعلاه تبدو غريبة. على سبيل المثال ، ستعزى الشكوى حول عدم وجود معلمة JSON إلى أن تحميل البيانات بتنسيق JSON أمر شائع جدًا - وربما يكون أكثر شيوعًا بين مستخدمي الطلبات من تحميل بيانات متعددة الأجزاء غير ملفات. المجادلة بأننا لن نقدمها لأن "نحن فقط أنواع فرعية ذات حالة خاصة من multipart " يبدو وكأنه شيء غريب يمكن قوله.

بغض النظر ، فإن جوهر المشكلة هو: API هو بيت القصيد من هذه المكتبة. إذا لم تتمكن من التوصل إلى طريقة "جميلة" لتنفيذ هذه الوظيفة ، فلن يحدث ذلك.

لن يكون هناك سبب لمثل هذه الشكوى [كذا]

@ piotr-dobrogost لديك أمل في مستقبل المطورين أكثر مما أفعل. أبعد من ذلك ، فإن المعلمة المطلوبة غير دقيقة. يجب أن يطلق عليه اسم form_data أكثر من multipart . كما لاحظت ، (على الرغم من أنه غير مباشر) multipart يمكن أن يشير إلى الكثير من أنواع الوسائط المختلفة.

إنه أمر محرج بينما لا ينبغي أن يكون كذلك.

لا يمكن أن تكون كل الأشياء أنيقة (حتى في لغة الثعبان).

ولا تدعمها

لكنها مدعومة. ولكن بعيدًا عن ذلك ، لدينا data مقابل application/x-www-form-urlencoded ، files (أو files+data ) مقابل multipart/form-data ، لماذا نحتاج إلى معلمة أخرى مقابل multipart/form-data عندما يكون لدينا؟

لا تحتاج إلى استخدام مزيج من data و files للحصول على النتيجة المرجوة. يمكنك فعل شيء مشابه لـ: requests.post('http://example.com/', files=[('key1', 'param1'), ('key2', 'param2')]) بدون وجود ملف فعلي هناك.

وإذا أردنا أن نكون دقيقين ، فقد لا يكون form_data واضحًا تمامًا ، فلماذا لا تستخدم المعلمة multipart_form_data ، ولكن الآن هذا أخرق أيضًا. إنه صريح ، نعم ، ويدعو PEP 8 للتوضيح ، لكن السلوك الحالي موثق جيدًا . إذا قررت kennethreitz قبول طلب الميزة هذا ، فكل ما تحتاجه المعلمة هو العمل كاسم مستعار لمعامل الملفات. لكن بالنظر إلى أن السلوك مدعوم بالفعل ، لا أعتقد أنه ضروري.

@ sigmavirus24 و Lukasa لخصا هذا الأمر تمامًا.

@ piotr-dobrogost مساهماتك موضع تقدير ، لكن ليس لهجتك. من فضلك توقف عن إصدار تأكيدات حازمة ضد مشروعنا وأهدافه.

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

أظن أن هذا سيستمر في كونه مشكلة للمستخدمين في المستقبل ، وأنا في حيرة من أمري لماذا لا يبدو أن هناك جهدًا لمعالجة هذا الأمر.

انظر ردي على # 935

شكرا. إنني أتطلع إلى ذلك!

تضمين التغريدة

_الطلبات لا تدعم أنواع MIME ، فهي تدعم حالات الاستخدام.

يعد إرسال بيانات multipart/form-data حالة استخدام شائعة.

_ (...) تحميل البيانات بتنسيق JSON شائع جدًا (...) _

لا يمكننا مقارنة إرسال json بإرسال multipart/form-data . إرسال json سهل ؛ قمت بتعيين Content-type ، قم بترميز البيانات في سطر واحد باستخدام وحدة مدمجة وهذا كل شيء. يعتبر إرسال multipart/form-data أكثر تعقيدًا لأن جسم الطلب يجب أن يكون له هيكل محدد. بمعنى آخر ، يعتبر إرسال json نوعًا من الشفافية بقدر ما يتعلق الأمر ببروتوكول HTTP ولكن إرسال multipart/form-data ليس كذلك. نظرًا لأن الطلبات هي مكتبة HTTP ، فيجب أن تهتم بإنشاء مثل هذا الهيكل.

_إذا لم تتمكن من التوصل إلى طريقة جميلة لتنفيذ هذه الوظيفة ، فلن يحدث ذلك ._

إن وجود معلمة files لإرسال multipart/form-data والتي لا علاقة لها بالملفات ليست جميلة بأي شكل من الأشكال (إنها قبيحة) ، لكنها تمكنت بطريقة ما من الوصول إلى قاعدة الرموز :). إن الخروج بشيء أقل قبحًا أمر سهل حقًا :)

@ sigmavirus24

_ (...) لكن السلوك الحالي موثق جيدًا ._

لا يوجد قدر من الوثائق يجعل واجهة برمجة التطبيقات (API) الجيدة من واجهة برمجة التطبيقات (API) السيئة. كلما كانت واجهة برمجة التطبيقات API أفضل كلما قلت حاجتها إلى التوثيق.

_يمكنك فعل شيء مشابه لـ (...] _

صحيح ، لكن هذا مضلل للغاية وغير بديهي. لقد وصفت الخطأ في استخدام files param لهذا في تعليقي السابق .

الخلاصة: للتوصل إلى شيء أفضل ، نحتاج إلى الاعتراف بأن واجهة برمجة التطبيقات الحالية سيئة فيما يتعلق بإرسال بيانات multipart/form-data .

تضمين التغريدة

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

أوافق وهذا هو سبب إنشاء الإصدار رقم 935.

تم إغلاق هذه القضية.

سامحني @ kennethreitz لكن spacecase لم أستخدم ملفًا في أي مكان في هذا المثال. لقد استخدمت معلمة files لأفعل ما تريده بالضبط. لو استخدمت ملفًا ، لكنت رأيت open('filename') .

@ sigmavirus24 ، للأسف هذا ليس ما أريده. لا يتعلق الأمر بما إذا كان العميل يقرأ من ملف أم لا. هذا ما يقال للخادم. في مثالك ، نص المنشور هو

Content-Disposition: form-data; name="key1"; filename="key1"
Content-Type: application/octet-stream

param1
--2f8732ee35564115a6c6e0c1032773e8
Content-Disposition: form-data; name="key2"; filename="key2"
Content-Type: application/octet-stream

param2
--2f8732ee35564115a6c6e0c1032773e8--

لاحظ استخدام filename= . إنها تخبر الخادم بأنه يتم إرسال ملف. في تطبيقات الويب ، أعمل مع هذا السلوك غير الصحيح ينتج عنه خطأ.

أنا آسف ، ولكن لكي تكون مغرورًا للغاية لتخبرني أن هذا هو بالضبط ما أريده يسيء إلي. جئت لتقديم الأفكار والمساعدة ، ويبدو أنك لا تريد أيضًا في هذه الحالة. هذا جيد ، لكن من فضلك لا تجعلني أشعر بأنني محط أنظار.

حسنًا ، يبدو أنني تذكرت السلوك بشكل غير صحيح. اسف بشأن ذلك. لم يكن في نيتي الإساءة إليك على الإطلاق أو جعلك تشعر بأنك محطمة. هذا بالتأكيد كافٍ ليجعلني أتأرجح في الحاجة إلى طريقة أفضل للتعامل مع multipart/form-data ، لكن في الوقت الحالي لا أوافق على أن أفكار API ليست أنيقة على الإطلاق.

spacecase ، يرجى الاطلاع على

spacecase كإيماءة لإظهار أن رأيك مهم ، تحقق من sigmavirus24 / طلبات البيانات-مخططات كإجراء لسد الفجوة. سيتعين عليك تعيين رأس Content-Type الخاص بك ، ولكن قد يكون ذلك مصدر إزعاج بسيط.

شكرًا @ sigmavirus24 ، سأحاول ذلك. يبدو أنه سيفي بالغرض.
أنا أقدر أنك لم تقصد الإساءة إلي. أشعر بتحسن حيال ذلك ولا أحمل أي شيء ضدك أو ضد المشروع.

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

لدي ملف وبعض القيم الأساسية للنشر. إذن ما هي الطريقة الصحيحة لإرسال مثل هذا الطلب متعدد الأجزاء؟ آمل أن تتمكن من مساعدتي.

Content-Disposition: form-data; name="up"; filename="aa.PNG"
Content-Type: image/png

file data
---------------------------7dee5302248e
Content-Disposition: form-data; name="exp"


-----------------------------7dee5302248e
Content-Disposition: form-data; name="ptext"

text
-----------------------------7dee5302248e
Content-Disposition: form-data; name="board"

DV_Studio
-----------------------------7dee5302248e--

I tried this way but only got a 504 error.
myfile=[('file',open('bb.jpg')),('exp','python'),('ptext',''),('board','DV_Studio')]
r = requests.post(url,files=myfile)

deerstalker للأسئلة الرجاء استخدام StackOverflow . للإجابة على سؤالك ، هناك مشروع قيد التنفيذ لمعالجة هذه المشكلة sigmavirus24 / Orders-toolbelt

لاحظ أيضًا أنني أجبت على هذا السؤال من قبل على StackOverflow ، كما ترون هنا .

لدي نفس مشكلة إنشاء مشاركات متعددة الأجزاء بدون ملف. في الوقت الحالي ، لا فرق إذا كنت تفعل requests.post(url, data=data_dict) أو requests.post(url, data=data_dict, files={}) . ولكن نظرًا لأن الكلمة الرئيسية files افتراضية هي None ، يجب أن نكون قادرين على امتلاك سلوكين مختلفين. A multipart/form-data عندما يتم تحديد files={} ، و application/x-www-form-urlencoded عندما لا يتم ذلك.

هل فاتني شيء؟

jwoillez هل اتبعت رابط تجاوز سعة المكدس الذي نشرته؟

أعتقد ذلك ، لكن إجابتك هناك تتعامل مع Multipart POST مع ملف واحد. عدت إلى الإصدار الأصلي: Multipart POST بدون ملف.

يُسمح لكائن الملف بأن يكون سلسلة. =) يجب أن يزودك ذلك بالمعلومات التي تريدها.

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

Content-Disposition: form-data; name="file"; filename="filename.txt"
Content-Type: text/plain

content
--3eeaadbfda0441b8be821bbed2962e4d--

حيث content هي السلسلة التي تدعوني لاستخدامها بدلاً من الملف؟

أنا حقا بعد هذا فقط:

Content-Disposition: form-data; name="key1"

value1
--3eeaadbfda0441b8be821bbed2962e4d

يمكن ترك الحقول في المجموعة التي لا تريدها كإعدادات افتراضية:

files = {'name': ('', 'content')}

في المستقبل ، يرجى توجيه أسئلتك إلى StackOverflow. يتتبعه جميع المشرفين بانتظام ، وهو المكان الأنسب لطرح هذه الأسئلة.

هذه هي الإجابة التي كنت أبحث عنها ، شكرًا. آسف على الضوضاء.

ربما سؤال أخير ، هل التالي ممكن (اسم ملف فارغ محدد ، محتوى فارغ)؟

Content-Disposition: form-data; name="file"; filename=""
Content-Type: text/plain


--3eeaadbfda0441b8be821bbed2962e4d--

يمكنك توفير محتوى فارغ باستخدام سلسلة فارغة في قسم المحتوى في المجموعة. لا يمكنك توفير اسم ملف فارغ حرفيًا ، ولكن يجب التعامل مع اسم ملف غير موجود بنفس الطريقة تمامًا.

كنت في بعض الأحيان بحاجة إلى القيام بذلك لأسباب غريبة مختلفة.
أقترح هذا الأسلوب لأي شخص يرغب في تسليح API في حالة الاستخدام هذه:

class ForceMultipartDict(dict):
    def __bool__(self):
        return True


FORCE_MULTIPART = ForceMultipartDict()  # An empty dict that boolean-evaluates as `True`.


client.post("/", data={"some": "data"}, files=FORCE_MULTIPART)

أو يمكنك استخدام حزام الأدوات وعدم اللجوء إلى الاختراقات.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات