Uploading files using Python requests module
Question: I need to load a file using a soap endpoint url...When I use the below code to
load it the files are getting loaded but they are not in a readable
format...When I load using SOAPUI tool it loads properly...

    
    
    import requests
    
    xml = '''<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                    xmlns:v1="http://s.sa.com/services/Attachment/v1.0">
       <soapenv:Header/>
       <soapenv:Body>
          <v1:attachment>
             <filename>FUZZY.csv</filename>
             <data>cid:138641430598</data>
          </v1:attachment>
       </soapenv:Body>
    </soapenv:Envelope>'''
    target_url =  'https://s.sa.com:443/soatest/FileAttachmentService'
    headers = {'Content-Type': 'text/xml','charset':'utf-8'}
    r = requests.post(target_url,data=xml,headers=headers,auth=('3user1',''))
    print 'r.text = ', r.text
    print 'r.content = ', r.content
    print 'r.status_code = ', r.status_code
    

New changes:-

    
    
    files = {'file':open('./FUZZY.csv','rb')}
    print files
    r = requests.post(target_url,files=files,data=xml,headers=headers,auth=('p3user1',''))
    

Error:

    
    
    Traceback (most recent call last):
      File "soapcall_python.py", line 18, in <module>
        r = requests.post(target_url,files=files,data=xml,headers=headers,auth=('p3user1',''))
      File "/opt/python2.7/lib/python2.7/site-packages/requests-2.3.0-py2.7.egg/requests/api.py", line 88, in post
        return request('post', url, data=data, **kwargs)
      File "/opt/python2.7/lib/python2.7/site-packages/requests-2.3.0-py2.7.egg/requests/api.py", line 44, in request
        return session.request(method=method, url=url, **kwargs)
      File "/opt/python2.7/lib/python2.7/site-packages/requests-2.3.0-py2.7.egg/requests/sessions.py", line 418, in request
        prep = self.prepare_request(req)
      File "/opt/python2.7/lib/python2.7/site-packages/requests-2.3.0-py2.7.egg/requests/sessions.py", line 356, in prepare_request
        hooks=merge_hooks(request.hooks, self.hooks),
      File "/opt/python2.7/lib/python2.7/site-packages/requests-2.3.0-py2.7.egg/requests/models.py", line 297, in prepare
        self.prepare_body(data, files)
      File "/opt/python2.7/lib/python2.7/site-packages/requests-2.3.0-py2.7.egg/requests/models.py", line 432, in prepare_body
        (body, content_type) = self._encode_files(files, data)
      File "/opt/python2.7/lib/python2.7/site-packages/requests-2.3.0-py2.7.egg/requests/models.py", line 109, in _encode_files
        raise ValueError("Data must not be a string.")
    ValueError: Data must not be a string.
    


Answer: You aren't sending the contents of the file anywhere. You're just sending a
reference to a file that doesn't exist anywhere that the server can see.

As the docs for [SOAP references to attachments](http://www.w3.org/TR/SOAP-
attachments#SOAPReferenceToAttachements) explains, the way you do this is to
send a MIME-multipart message. If you're using the CID reference mechanism,
that `cid` isn't some arbitrary string, it has to match the `Content-ID`
header of a message in the MIME envelope.

The `requests` docs for [POST a Multipart-Encoded File](http://docs.python-
requests.org/en/latest/user/quickstart/#post-a-multipart-encoded-file) explain
how to send the contents of a file as a message within a MIME request;
briefly:

    
    
    with open('FUZZY.csv', 'rb') as f:
        files = {'file': f}
        r = requests.post(target_url,
                          data=xml, headers=headers, auth=('3user1',''), 
                          files=files)
    

However, this simple method doesn't give you access to the Content-ID that
will be generated under the covers for your message. So, if you want to use
the CID reference mechanism, you will need to generate the MIME envelope
manually (e.g., by using
[`email.mail.MIMEMultipart`](https://docs.python.org/3.4/library/email.mime.html))
and sending the entire thing as a `data` string.

