Search code examples
pythonrestpostpython-requestsmultipartform-data

Multipart/form request in Python results in TypeError


I am trying to send an image to a OpenProject Workpackage via it's Rest-API. The documentation to the specific endpoint can be found here: https://www.openproject.org/docs/api/endpoints/attachments/ (/api/v3/work_packages/{id}/attachments is the endpoint I want to use.) The Specification of the request is detailed and I prototyped the request in Postman and got it to work. In the documentation it says:

To add an attachment to a work package, a client needs to issue a request of type multipart/form-data with exactly two parts. The first part must be called metadata. Its content type is expected to be application/json, the body must be a single JSON object, containing at least the fileName and optionally the attachments description. The second part must be called file, its content type should match the mime type of the file. The body must be the raw content of the file. Note that a filename must be indicated in the Content-Disposition of this part, however it will be ignored. Instead the fileName inside the JSON of the metadata part will be used.

Now I want to implement the request in Python using Flask and the requests library. This is my code:

filename = "image{}.png".format(index)
with open(filename, 'rb') as file:
    multipart_form = {
        "metadata": {"fileName", filename, "application/json"},
        "file": (None, file.read(), "image/png")
    }
res = requests.post(url=url, files=multipart_form, headers=header)
return res

executing this request results in the following error message:

TypeError: a bytes-like object is required, not 'set'

the index variable in line 1 is just incremented and passed into. Also it is already of type string when entering this snippet of code. The file is present on the filesystem when this snippet is called. I guess the error is with the formatting of the multipart/form request. But I can't figure out how to make this work.


Solution

  • I've been struggling with something similar for a while and could attach a file to a work package using urllib3 and OrderedDict, like this:

    import urllib3
    from collections import OrderedDict
    
    filename = "image{}.png".format(index)
    with open(filename, 'rb') as file:
        multipart_form = OrderedDict([
            ( 'metadata', '{ "fileName" : "' + filename + '" }' ),
            ( 'file' , ( filename, file.read(), "image/png" ) )
        ])
    
    body, content = urllib3.encode_multipart_formdata( multipart_form )
    headers = urllib3.util.make_headers( basic_auth = auth, accept_encoding ='application/json' )
    headers['Content-Type'] = content
    res = http.request( 'POST', url, headers = myHeaders, body = body )
    return res
    

    Hope it helps!