How to send a “multipart/form-data” with requests in python?

前端 未结 9 1737
野趣味
野趣味 2020-11-22 01:29

How to send a multipart/form-data with requests in python? How to send a file, I understand, but how to send the form data by this method can not understand.

相关标签:
9条回答
  • 2020-11-22 01:39

    Send multipart/form-data key and value

    curl command:

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

    python requests - More complicated POST requests:

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

    Send multipart/form-data file

    curl command:

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

    python requests - POST a Multipart-Encoded File:

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

    that's all.

    0 讨论(0)
  • 2020-11-22 01:41

    You need to use the name attribute of the upload file that is in the HTML of the site. Example:

    autocomplete="off" name="image">
    

    You see name="image">? You can find it in the HTML of a site for uploading the file. You need to use it to upload the file with Multipart/form-data

    script:

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

    Here, in the place of image, add the name of the upload file in HTML

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

    If the upload requires to click the button for upload, you can use like that:

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

    Then start the request

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

    And done, file uploaded succesfully

    0 讨论(0)
  • 2020-11-22 01:42

    You need to use the files parameter to send a multipart form POST request even when you do not need to upload any files.

    From the original requests source:

    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.
    

    The relevant part is: file-tuple can be a2-tuple, 3-tupleor a4-tuple.

    Based on the above, the simplest multipart form request that includes both files to upload and form fields will look like this:

    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)
    

    Note the None as the first argument in the tuple for plain text fields — this is a placeholder for the filename field which is only used for file uploads, but for text fields passing None as the first parameter is required in order for the data to be submitted.

    Multiple fields with the same name

    If you need to post multiple fields with the same name then instead of a dictionary you can define your payload as a list (or a tuple) of tuples:

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

    Streaming requests API

    If the above API is not pythonic enough for you, then consider using requests toolbelt (pip install requests_toolbelt) which is an extension of the core requests module that provides support for file upload streaming as well as the MultipartEncoder which can be used instead of files, and which also lets you define the payload as a dictionary, tuple or list.

    MultipartEncoder can be used both for multipart requests with or without actual upload fields. It must be assigned to the data parameter.

    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})
    

    If you need to send multiple fields with the same name, or if the order of form fields is important, then a tuple or a list can be used instead of a dictionary:

    multipart_data = MultipartEncoder(
        fields=(
                ('action', 'ingest'), 
                ('item', 'spam'),
                ('item', 'sausage'),
                ('item', 'eggs'),
               )
        )
    
    0 讨论(0)
  • 2020-11-22 01:53

    Basically, if you specify a files parameter (a dictionary), then requests will send a multipart/form-data POST instead of a application/x-www-form-urlencoded POST. You are not limited to using actual files in that dictionary, however:

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

    and httpbin.org lets you know what headers you posted with; in response.json() we have:

    >>> 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'}
    

    Better still, you can further control the filename, content type and additional headers for each part by using a tuple instead of a single string or bytes object. The tuple is expected to contain between 2 and 4 elements; the filename, the content, optionally a content type, and an optional dictionary of further headers.

    I'd use the tuple form with None as the filename, so that the filename="..." parameter is dropped from the request for those parts:

    >>> 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 can also be a list of two-value tuples, if you need ordering and/or multiple fields with the same name:

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

    If you specify both files and data, then it depends on the value of data what will be used to create the POST body. If data is a string, only it willl be used; otherwise both data and files are used, with the elements in data listed first.

    There is also the excellent requests-toolbelt project, which includes advanced Multipart support. It takes field definitions in the same format as the files parameter, but unlike requests, it defaults to not setting a filename parameter. In addition, it can stream the request from open file objects, where requests will first construct the request body in memory:

    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}
    )
    

    Fields follow the same conventions; use a tuple with between 2 and 4 elements to add a filename, part mime-type or extra headers. Unlike the files parameter, no attempt is made to find a default filename value if you don't use a tuple.

    0 讨论(0)
  • 2020-11-22 01:58

    Here is the python snippet you need to upload one large single file as multipart formdata. With NodeJs Multer middleware running on the server side.

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

    For the server side please check the multer documentation at: https://github.com/expressjs/multer here the field single('fieldName') is used to accept one single file, as in:

    var upload = multer().single('fieldName');
    
    0 讨论(0)
  • 2020-11-22 01:59

    Since the previous answers were written, requests have changed. Have a look at the bug thread at Github for more detail and this comment for an example.

    In short, the files parameter takes a dict with the key being the name of the form field and the value being either a string or a 2, 3 or 4-length tuple, as described in the section POST a Multipart-Encoded File in the requests quickstart:

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

    In the above, the tuple is composed as follows:

    (filename, data, content_type, headers)
    

    If the value is just a string, the filename will be the same as the key, as in the following:

    >>> files = {'obvius_session_id': '72c2b6f406cdabd578c5fd7598557c52'}
    
    Content-Disposition: form-data; name="obvius_session_id"; filename="obvius_session_id"
    Content-Type: application/octet-stream
    
    72c2b6f406cdabd578c5fd7598557c52
    

    If the value is a tuple and the first entry is None the filename property will not be included:

    >>> files = {'obvius_session_id': (None, '72c2b6f406cdabd578c5fd7598557c52')}
    
    Content-Disposition: form-data; name="obvius_session_id"
    Content-Type: application/octet-stream
    
    72c2b6f406cdabd578c5fd7598557c52
    
    0 讨论(0)
提交回复
热议问题