Python Requests Giving Me Missing Metadata When Trying To Upload Attachment

107 Views Asked by At

I am trying to upload an attachment via an API. I can make it work in the software's Swagger environment with this:

curl -X 'POST' \
  'https://demo.citywidesolutions.com/v4_server/external/v1/maintenance/service_requests/9/attached_files' \
  -H 'accept: */*' \
  -H 'Content-Type: multipart/form-data' \
  -H 'Authorization: Bearer 123456789' \
  -F 'description=' \
  -F 'data=@This is my file.pdf;type=application/pdf'

When I try to do the same with python requests I get a 400 error with a message of missing metadata. Here is what I am trying to pass with Python:

import requests
Headers = {'accept': '*/*', 'Content-Type': 'multipart/form-data', 'Authorization': 'Bearer 123456789'}
Attachment = {'description': '', 'data': open('C:/This is my file.pdf', 'rb')}
Response = requests.post(url='https://demo.citywidesolutions.com/v4_server/external/v1/maintenance/service_requests/9/attached_files', headers=Headers, files=Attachment)

From that I get a 400 response and the JSON says Missing Metadata. What am I missing here?

2

There are 2 best solutions below

3
thetaco On BEST ANSWER

You could try explicitly setting the file type in your code; you do so in the curl method, but not in the Python script.

Also, the requests library is able to handle 'Content-Type', so typically you don't need to set it. And the description parameter may be expected as a tuple. Here are all those changes; I have not tested since I do not have access to the endpoint:

import requests

headers = {
    'accept': '*/*',
    'Authorization': 'Bearer 123456789'
} # excluded content-type

files = {
    'description': (None, ''),
    'data': ('blank.pdf', open('blank.pdf', 'rb'), 'application/pdf') #added application/pdf format
}

url = 'https://demo.citywidesolutions.com/v4_server/external/v1/maintenance/service_requests/9/attached_files'

response = requests.post(url, headers=headers, files=files)
3
NeuroWinter On

I believe that the issue here is that you are providing an object as you data.

The open() function will return a bufferedReader object and not the actual data of the file.

See:

>>> open("README.md", 'rb')
<_io.BufferedReader name='README.md'>

I think you can fix this by changing

Attachment = {'description': '', data=open('C:/This is my file.pdf', 'rb')}

to

Attachment = {'description': '', data=open('C:/This is my file.pdf', 'rb').read()}

Also note that it is normally best practice to have your variable names all in lowercase.

If that does not work then you can try using the files parameter of post() like this:

>>> url = 'https://httpbin.org/post'
>>> files = {'file': open('report.xls', 'rb')}
>>> r = requests.post(url, files=files)

(Example shamelessly stolen from https://requests.readthedocs.io/en/latest/user/quickstart/#post-a-multipart-encoded-file)