[Python] python requests: multipart/form-data and a generator

Discussão em 'Python' iniciado por Stack, Junho 21, 2025.

  1. Stack

    Stack Membro Participativo

    I used a generator as an argument to data for Content-Type application/json, and that worked fine.

    The problem now is that I need to do this for multipart/form-data, and it appears to not support a generator.


    • I tried files={'content': my_generator_func()} and got: "a bytes-like object is required, not 'generator'"; so it looks like it would have accepted a file descriptor, but not a generator.


    • I then tried a generator for the data argument, and set the files argument to a form name and value: files={'encoding': (None, 'base64')} [I read you have to use the files argument to get requests to do multipart/form-data] For that I get the error "Streamed bodies and files are mutually exclusive".


    • Just in case, I tried it different ways setting a header for Content-Type multipart/form-data, both with and without it [I had read that setting it yourself can mess up requests.] If I omitted it, it complained it was missing; and if I included it, I got the errors previously specified.


    • The MultipartEncoder helper, while it does accept a file descriptor, does not accept a generator.

    Is it possible to furnish a generator for multipart/form-data? If I have to, I will dump the stream into a file and pass a descriptor to the files argument; but seems that you would have furnished a way to use a generator since it is supported for Content-Type application/json

    PS The closest StackOverflow post on this topic is: How to send a "multipart/form-data" with requests in python?

    PPS I also tried this:

    https://toolbelt.readthedocs.io/en/latest/uploading-data.html#streaming-data-from-a-generator

    and the endpoint is not seeing the form data being sent. [I confirmed it works with curl using --form name=value arguments.]

    I was asked for a code example:

    def my_generator():
    'when iterated over, this will return "value_fragment1;value_fragment2;"'
    yield 'value_fragment1;'
    yield 'value_fragment2;'

    response=requests.post(url,
    # tried with+without this header...
    headers={'Content-Type': 'multipart/form-data'},
    data={'field1': (None, 'value1'),
    'field2': (None, 'value2')
    }, # the data argument is done the way the Stack post suggests for multipart...
    files={'field3', my_generator()}
    )
    # this code generates the generator not expected error...


    And using the Streaming iterator:

    from requests_toolbelt.streaming_iterator import StreamingIterator
    # .
    # .
    # .
    # this is done just to get a length
    # for the content in this example;
    # in a real implementation we would calculate this...
    content=''
    for fragment in my_generator():
    content+=fragment

    streamer=StreamingIterator(len(content), my_generator())
    response=requests.post(url,
    # tried with+without this header...
    headers={'Content-Type': 'multipart/form-data'},
    data={'field1': (None, 'value1'),
    'field2': (None, 'value2')
    }, # the data argument is done the way the Stack post suggests for multipart...
    files={'field3', streamer}
    )
    # the endpoint does not see the fields,
    # while it sees them for a curl --form field1=value1 --form field2=value2 ...


    =======================================

    Tried this and plugged it into the MultipartEncoder: How do I read a tarfile from a generator? It is not as simple as that post thinks; you need to implement seeking functions in your BufferedReader/RawIOBase class:

    File "/usr/lib/python3/dist-packages/requests_toolbelt/multipart/encoder.py", line 573, in len
    return total_len(self.fd) - self.fd.tell()
    ^^^^^^^^^^^^^^
    io.UnsupportedOperation: seek


    I also tried passing a StreamingIterator to a MultipartEncoder class (where you would pass a file descriptor...), but it hangs on the request.

    Looks like implementation of a generator for multipart/form-data was an oversight by the requests [toolbelt] developers...

    Continue reading...

Compartilhe esta Página