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

前端 未结 9 1786
野趣味
野趣味 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: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 `.
    
        ...
        :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'),
               )
        )
    

提交回复
热议问题