अजगर में अनुरोधों के साथ "मल्टीपार्ट / फॉर्म-डेटा" कैसे भेजें?


211

multipart/form-dataअजगर में अनुरोधों के साथ कैसे भेजें ? फ़ाइल कैसे भेजें, मुझे समझ में आता है, लेकिन इस विधि से फ़ॉर्म डेटा कैसे भेज सकते हैं, यह समझ में नहीं आता है।


आपका प्रश्न वास्तव में स्पष्ट नहीं है। आपकी क्या प्राप्त करने की इच्छा है? क्या आप प्रपत्र में फ़ाइल अपलोड किए बिना "मल्टीपार्ट / फॉर्म-डेटा" भेजना चाहते हैं?
हंस फिर

4
तथ्य यह है कि filesपैरामीटर का उपयोग दोनों को करने के लिए किया जाता है एक बहुत खराब एपीआई है। मैंने मल्टीपार्ट डेटा भेजते हुए शीर्षक जारी किया - इसे ठीक करने के लिए हमें बेहतर API की आवश्यकता है । यदि आप सहमत हैं कि filesmulitpart डेटा भेजने के लिए पैरामीटर का उपयोग करना भ्रामक है, तो कृपया उपरोक्त समस्या में API को बदलने के लिए कहें।
पायोत्र डोब्रोगोस्ट नोव

@PiotrDobrogost जो समस्या बंद है। लोगों को बंद मुद्दों पर टिप्पणी करने के लिए प्रोत्साहित न करें, प्रासंगिक या अन्यथा।
इयान स्टैपलटन कॉर्डैस्को

1
कोई बात नहीं, मुझे एहसास हुआ कि आपकी टिप्पणी को बंद करने से पहले पोस्ट किया गया था। मैं नफरत करता हूं कि कैसे StackOverflow चीजों को कालानुक्रमिक क्रम में नहीं रखता है।
इयान स्टैपलटन कॉर्डैस्को

जवाबों:


166

मूल रूप से, यदि आप एक filesपैरामीटर (एक शब्दकोश) निर्दिष्ट करते हैं , तो requestsएक multipart/form-dataPOST के बजाय एक POST भेज देगा application/x-www-form-urlencoded। आप उस शब्दकोश में वास्तविक फ़ाइलों का उपयोग करने के लिए सीमित नहीं हैं, हालांकि:

>>> import requests
>>> response = requests.post('http://httpbin.org/post', files=dict(foo='bar'))
>>> response.status_code
200

और httpbin.org आपको बताता है कि आपने किस हेडर के साथ पोस्ट किया है; में response.json()हमने:

>>> from pprint import pprint
>>> pprint(response.json()['headers'])
{'Accept': '*/*',
 'Accept-Encoding': 'gzip, deflate',
 'Connection': 'close',
 'Content-Length': '141',
 'Content-Type': 'multipart/form-data; '
                 'boundary=c7cbfdd911b4e720f1dd8f479c50bc7f',
 'Host': 'httpbin.org',
 'User-Agent': 'python-requests/2.21.0'}

बेहतर अभी भी, आप एकल स्ट्रिंग या बाइट्स ऑब्जेक्ट के बजाय टपल का उपयोग करके प्रत्येक भाग के लिए फ़ाइल नाम, सामग्री प्रकार और अतिरिक्त हेडर को नियंत्रित कर सकते हैं। टपल में 2 और 4 तत्वों के बीच होने की उम्मीद है; फ़ाइल नाम, सामग्री, वैकल्पिक रूप से एक सामग्री प्रकार और आगे हेडर का एक वैकल्पिक शब्दकोश।

मैं Noneफ़ाइल नाम के साथ tuple फ़ॉर्म का उपयोग करूंगा , ताकि filename="..."उन भागों के अनुरोध से पैरामीटर हटा दिया जाए:

>>> files = {'foo': 'bar'}
>>> print(requests.Request('POST', 'http://httpbin.org/post', files=files).prepare().body.decode('utf8'))
--bb3f05a247b43eede27a124ef8b968c5
Content-Disposition: form-data; name="foo"; filename="foo"

bar
--bb3f05a247b43eede27a124ef8b968c5--
>>> files = {'foo': (None, 'bar')}
>>> print(requests.Request('POST', 'http://httpbin.org/post', files=files).prepare().body.decode('utf8'))
--d5ca8c90a869c5ae31f70fa3ddb23c76
Content-Disposition: form-data; name="foo"

bar
--d5ca8c90a869c5ae31f70fa3ddb23c76--

files यदि आप एक ही नाम के साथ आदेश और / या कई क्षेत्रों की आवश्यकता हो, तो दो-मूल्य वाले टुपल्स की एक सूची भी हो सकती है:

requests.post(
    'http://requestb.in/xucj9exu',
    files=(
        ('foo', (None, 'bar')),
        ('foo', (None, 'baz')),
        ('spam', (None, 'eggs')),
    )
)

तुम दोनों का उल्लेख करते हैं filesऔर dataहै, तो यह इस पर निर्भर करता मूल्य का dataक्या पोस्ट शरीर बनाने के लिए इस्तेमाल किया जाएगा। यदि dataएक तार है, केवल इसका उपयोग किया जाएगा; अन्यथा दोनों dataऔर पहले filesसे dataसूचीबद्ध तत्वों के साथ उपयोग किए जाते हैं ।

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

from requests_toolbelt.multipart.encoder import MultipartEncoder

mp_encoder = MultipartEncoder(
    fields={
        'foo': 'bar',
        # plain file object, no filename or mime type produces a
        # Content-Disposition header with just the part name
        'spam': ('spam.txt', open('spam.txt', 'rb'), 'text/plain'),
    }
)
r = requests.post(
    'http://httpbin.org/post',
    data=mp_encoder,  # The MultipartEncoder is posted as data, don't use files=...!
    # The MultipartEncoder provides the content-type header with the boundary:
    headers={'Content-Type': mp_encoder.content_type}
)

फ़ील्ड एक ही सम्मेलनों का पालन करते हैं; एक फ़ाइल नाम, भाग माइम-प्रकार या अतिरिक्त हेडर जोड़ने के लिए 2 और 4 तत्वों के बीच एक टपल का उपयोग करें। filesपैरामीटर के विपरीत , filenameयदि आप टुपल का उपयोग नहीं करते हैं तो कोई डिफ़ॉल्ट मान खोजने का प्रयास नहीं किया जाता है ।


3
यदि फ़ाइलें = {} का उपयोग किया जाता है, तो हेडर = {'सामग्री-प्रकार': 'blah blah'} का उपयोग नहीं किया जाना चाहिए!
ज़की

5
@ ज़ाकी: वास्तव में, multipart/form-dataसामग्री-प्रकार को रोकें, पोस्ट बॉडी में भागों को सीमांकित करने के लिए उपयोग की जाने वाली सीमा मूल्य शामिल होना चाहिए । सेट नहीं करके Content-Typeहैडर सुनिश्चित है कि requestsयह सही मान को सेट करता है।
मार्टिन पीटर्स

महत्वपूर्ण नोट: अनुरोध केवल तभी भेजा जाएगा जैसे multipart/form-dataकि मान files=सत्य है, इसलिए यदि आपको एक multipart/form-dataअनुरोध भेजने की आवश्यकता है, लेकिन किसी भी फाइल में शामिल नहीं हैं, तो आप एक सत्य लेकिन अर्थहीन मान सेट कर सकते हैं जैसे कि {'':''}, और data=आपके अनुरोध निकाय के साथ सेट करें। यदि आप ऐसा कर रहे हैं, तो Content-Typeहेडर को स्वयं प्रदान न करें; requestsआपके लिए इसे सेट कर देगा। आप यहाँ सत्य की जाँच देख सकते हैं: github.com/psf/requests/blob/…
डैनियल सितुनायके

@DanielSitunayake को इस तरह के हैक की कोई आवश्यकता नहीं है। बस सभी फ़ील्ड्स को filesडिक्टेट में रखें, उन्हें फाइल करने की ज़रूरत नहीं है (बस टपल फॉर्म का उपयोग करना और फ़ाइलनाम सेट करना सुनिश्चित करें None)। बेहतर अभी भी, requests_toolbeltपरियोजना का उपयोग करें ।
मार्टिन पीटर्स

धन्यवाद @MartijnPieters, टुप फॉर्म के साथ ट्रिक बहुत बढ़िया है! कोशिश करेंगे कि।
डैनियल सितुनायके

107

चूंकि पिछले उत्तर लिखे गए थे, इसलिए अनुरोध बदल गए हैं। अधिक विस्तार के लिए जीथब पर बग थ्रेड पर एक नज़र डालें और एक उदाहरण के लिए यह टिप्पणी करें

संक्षेप में, फ़ाइल पैरामीटर dictकुंजी फ़ील्ड फॉर्म का नाम होने के साथ एक मूल्य लेता है और मूल्य या तो एक स्ट्रिंग या 2, 3 या 4-लंबाई टपल है, जैसा कि अनुभाग POST में एक मल्टीपार्ट-एन्कोडेड फ़ाइल में वर्णित है। जल्दी शुरू:

>>> url = 'http://httpbin.org/post'
>>> files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})}

उपरोक्त में, टपल की रचना इस प्रकार है:

(filename, data, content_type, headers)

यदि मान सिर्फ एक स्ट्रिंग है, तो फ़ाइल नाम कुंजी के समान होगा, जैसा कि निम्नलिखित में है:

>>> files = {'obvius_session_id': '72c2b6f406cdabd578c5fd7598557c52'}

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

72c2b6f406cdabd578c5fd7598557c52

यदि मान एक टुपल है और पहली प्रविष्टि Noneफ़ाइल नाम संपत्ति में शामिल नहीं होगी:

>>> files = {'obvius_session_id': (None, '72c2b6f406cdabd578c5fd7598557c52')}

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

72c2b6f406cdabd578c5fd7598557c52

2
क्या होगा अगर आप को अलग करने की जरूरत है nameऔर filenameयह भी, लेकिन एक ही नाम के साथ कई क्षेत्रों है?
माइकल

1
मुझे @Michael के रूप में सिमिलर की समस्या है। क्या आप प्रश्न पर एक नज़र डाल सकते हैं और कुछ सुझा सकते हैं? [लिंक] ( stackoverflow.com/questions/30683352/… )
शारदूल

किसी ने एक ही नाम के साथ कई क्षेत्रों होने के साथ इस समस्या को हल किया?
14

1
खाली स्ट्रिंग पास करने की चाल के रूप में एक filesटुपल का पहला मूल्य अब काम नहीं करता है: आपको requests.post dataअतिरिक्त गैर-फ़ाइल multipart/form-dataपैरामीटर भेजने के लिए पैरामीटर का उपयोग करने की आवश्यकता है
लुकास साइमन

1
Noneखाली स्ट्रिंग के बजाय पास होना काम करने लगता है
अलेक्जेंड्रे ब्लिन

73

जब आप किसी भी फाइल को अपलोड करने की आवश्यकता नहीं है तब भी आपको filesएक मल्टीपार्ट फॉर्म POST अनुरोध भेजने के लिए पैरामीटर का उपयोग करने की आवश्यकता होती है ।

मूल अनुरोध स्रोत से:

def request(method, url, **kwargs):
    """Constructs and sends a :class:`Request <Request>`.

    ...
    :param files: (optional) Dictionary of ``'name': file-like-objects``
        (or ``{'name': file-tuple}``) for multipart encoding upload.
        ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``,
        3-tuple ``('filename', fileobj, 'content_type')``
        or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``,
        where ``'content-type'`` is a string
        defining the content type of the given file
        and ``custom_headers`` a dict-like object 
        containing additional headers to add for the file.

प्रासंगिक हिस्सा है: file-tuple can be a2-tuple, ।3-tupleor a4-tuple

उपरोक्त के आधार पर, सरलतम मल्टीपार्ट फ़ॉर्म अनुरोध जिसमें अपलोड करने के लिए दोनों फाइलें शामिल हैं और फॉर्म फ़ील्ड इस तरह दिखाई देंगे:

multipart_form_data = {
    'file2': ('custom_file_name.zip', open('myfile.zip', 'rb')),
    'action': (None, 'store'),
    'path': (None, '/path1')
}

response = requests.post('https://httpbin.org/post', files=multipart_form_data)

print(response.content)

Fields सादे पाठ फ़ील्ड के लिए टपल में पहले तर्क के Noneरूप में ध्यान दें - यह फ़ाइल नाम फ़ील्ड के लिए एक प्लेसहोल्डर है जो केवल फ़ाइल अपलोड के लिए उपयोग किया जाता है, लेकिन Noneडेटा सबमिट करने के लिए पहले पैरामीटर के रूप में पास होने वाले टेक्स्ट फ़ील्ड की आवश्यकता होती है ।

एक ही नाम के साथ कई फ़ील्ड

यदि आपको एक ही नाम के साथ कई फ़ील्ड पोस्ट करने की आवश्यकता है, तो एक शब्दकोश के बजाय आप अपने पेलोड को टुपल्स की एक सूची (या टुपल) के रूप में परिभाषित कर सकते हैं:

multipart_form_data = (
    ('file2', ('custom_file_name.zip', open('myfile.zip', 'rb'))),
    ('action', (None, 'store')),
    ('path', (None, '/path1')),
    ('path', (None, '/path2')),
    ('path', (None, '/path3')),
)

स्ट्रीमिंग अनुरोध एपीआई

यदि उपरोक्त API आपके लिए पर्याप्त पाइथोनिक नहीं है, तो अनुरोध टूलबेल्ट ( pip install requests_toolbelt) का उपयोग करने पर विचार करें जो कि कोर अनुरोध मॉड्यूल का एक विस्तार है जो फ़ाइल अपलोड स्ट्रीमिंग के लिए समर्थन प्रदान करता है और साथ ही मल्टीपार्ट एंकोडर जिसका उपयोग किया जा सकता है filesऔर जो इसकी अनुमति भी देता है आप पेलोड को डिक्शनरी, टपल या सूची के रूप में परिभाषित करते हैं।

MultipartEncoderवास्तविक अपलोड फ़ील्ड्स के साथ या बिना मल्टीपार्ट अनुरोधों के लिए दोनों का उपयोग किया जा सकता है। इसे dataपैरामीटर को सौंपा जाना चाहिए ।

import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder

multipart_data = MultipartEncoder(
    fields={
            # a file upload field
            'file': ('file.zip', open('file.zip', 'rb'), 'text/plain')
            # plain text fields
            'field0': 'value0', 
            'field1': 'value1',
           }
    )

response = requests.post('http://httpbin.org/post', data=multipart_data,
                  headers={'Content-Type': multipart_data.content_type})

यदि आपको एक ही नाम के साथ कई फ़ील्ड भेजने की आवश्यकता है, या यदि फ़ॉर्म फ़ील्ड का क्रम महत्वपूर्ण है, तो एक शब्द के बजाय एक टपल या सूची का उपयोग किया जा सकता है:

multipart_data = MultipartEncoder(
    fields=(
            ('action', 'ingest'), 
            ('item', 'spam'),
            ('item', 'sausage'),
            ('item', 'eggs'),
           )
    )

इसके लिए शुक्रिया। चाबियों का क्रम मेरे लिए महत्वपूर्ण था और इससे बहुत मदद मिली।
स्प्लेंडर 16

गजब का। अस्पष्ट रूप से, एक एपीआई जिसके साथ मैं काम कर रहा हूं उसे एक ही कुंजी के लिए 2 अलग-अलग मानों की आवश्यकता होती है। ये अद्भुत है। धन्यवाद।
अंजोन

@ccpizza, वास्तव में इस लाइन का क्या मतलब है? > "('file.py', open ('file.py', 'rb'), 'text / plain')"। यह मेरे लिए काम नहीं करता है :(
डेनिस कोरेयबा

@DenisKoreyba: यह फ़ाइल अपलोड फ़ील्ड का एक उदाहरण है जो मानता है कि नाम वाली फ़ाइल file.pyआपकी स्क्रिप्ट के समान फ़ोल्डर में स्थित है।
ccpizza

1
आप Noneखाली स्ट्रिंग के बजाय उपयोग कर सकते हैं । तब अनुरोधों में एक फ़ाइल नाम शामिल नहीं होगा। तो इसके बजाय Content-Disposition: form-data; name="action"; filename=""यह होगा Content-Disposition: form-data; name="action"। यह सर्वर के लिए महत्वपूर्ण था कि मैं उन फील्ड्स को फॉर्म फील्ड के रूप में स्वीकार करूं न कि फाइलों के रूप में।
Mitar

8

अनुरोधों का उपयोग करके अतिरिक्त मापदंडों के साथ एक फ़ाइल अपलोड करने के लिए सरल कोड स्निपेट यहां दिया गया है:

url = 'https://<file_upload_url>'
fp = '/Users/jainik/Desktop/data.csv'

files = {'file': open(fp, 'rb')}
payload = {'file_id': '1234'}

response = requests.put(url, files=files, data=payload, verify=False)

कृपया ध्यान दें कि आपको किसी भी प्रकार की सामग्री को स्पष्ट रूप से निर्दिष्ट करने की आवश्यकता नहीं है।

नोट: उपरोक्त उत्तरों में से एक पर टिप्पणी करना चाहता था, लेकिन कम प्रतिष्ठा के कारण ऐसा नहीं कर सका, इसलिए यहां एक नई प्रतिक्रिया का मसौदा तैयार किया गया।


4

आपको nameअपलोड फ़ाइल की विशेषता का उपयोग करने की आवश्यकता है जो साइट के HTML में है। उदाहरण:

autocomplete="off" name="image">

आप देखते हैं name="image">? आप इसे फ़ाइल अपलोड करने के लिए किसी साइट के HTML में पा सकते हैं। फ़ाइल के साथ अपलोड करने के लिए आपको इसका उपयोग करने की आवश्यकता हैMultipart/form-data

स्क्रिप्ट:

import requests

site = 'https://prnt.sc/upload.php' # the site where you upload the file
filename = 'image.jpg'  # name example

यहां, छवि के स्थान पर, HTML में अपलोड फ़ाइल का नाम जोड़ें

up = {'image':(filename, open(filename, 'rb'), "multipart/form-data")}

यदि अपलोड के लिए अपलोड के लिए बटन पर क्लिक करना है, तो आप उसका उपयोग कर सकते हैं:

data = {
     "Button" : "Submit",
}

फिर अनुरोध शुरू करें

request = requests.post(site, files=up, data=data)

और किया, फ़ाइल सफलतापूर्वक अपलोड की गई


3

मल्टीपार्ट / फॉर्म-डेटा की और वैल्यू भेजें

कर्ल कमांड:

curl -X PUT http://127.0.0.1:8080/api/xxx ...
-H 'content-type: multipart/form-data; boundary=----xxx' \
-F taskStatus=1

अजगर के अनुरोध - अधिक जटिल पोस्ट अनुरोध :

    updateTaskUrl = "http://127.0.0.1:8080/api/xxx"
    updateInfoDict = {
        "taskStatus": 1,
    }
    resp = requests.put(updateTaskUrl, data=updateInfoDict)

मल्टीपार्ट / फॉर्म-डेटा फ़ाइल भेजें

कर्ल कमांड:

curl -X POST http://127.0.0.1:8080/api/xxx ...
-H 'content-type: multipart/form-data; boundary=----xxx' \
-F file=@/Users/xxx.txt

अजगर अनुरोध - पोस्ट एक मल्टीपार्ट-एन्कोडेड फ़ाइल :

    filePath = "/Users/xxx.txt"
    fileFp = open(filePath, 'rb')
    fileInfoDict = {
        "file": fileFp,
    }
    resp = requests.post(uploadResultUrl, files=fileInfoDict)

बस इतना ही।


-1

यहाँ अजगर स्निपेट है जिसे आपको मल्टीपर्ट फॉर्मडाटा के रूप में एक बड़ी एकल फ़ाइल अपलोड करने की आवश्यकता है। सर्वर साइड पर NodeJs Multer मिडलवेयर चल रहा है।

import requests
latest_file = 'path/to/file'
url = "http://httpbin.org/apiToUpload"
files = {'fieldName': open(latest_file, 'rb')}
r = requests.put(url, files=files)

सर्वर साइड के लिए कृपया मुलर डॉक्युमेंटेशन की जाँच करें: https://github.com/expressjs/multer यहाँ फ़ील्ड सिंगल ('फ़ील्डनेम') का उपयोग सिंगल फाइल को स्वीकार करने के लिए किया जाता है, जैसे:

var upload = multer().single('fieldName');
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.