Uploading files using Python requests module - python

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.

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 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 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) and sending the entire thing as a data string.

Related

How should I configure my headers to make an HTTP/2 POST to APNs to avoid "Received duplicate pseudo-header field" error?

I'm pretty new to HTTP stuff, primarily stick to iOS so please bear with me.
I'm using the httpx python library to try and send a notification to an iPhone because I have to make an HTTP/2 POST to do so. Apple's Documentation says it requires ":method" and ":path" headers but I when I try to make the POST with these headers included,
headers = {
':method' : 'POST',
':path' : '/3/device/{}'.format(deviceToken),
...
}
I get the error
h2.exceptions.ProtocolError: Received duplicate pseudo-header field b':path
It's pretty apparent there's a problem with having the ":path" header included but I'm also required to send it so I'm not sure what I'm doing wrong. Apple's Documentation also says to
Encode the :path and authorization values as literal header fields without indexing.
Encode all other fields as literal header fields with incremental indexing.
I really don't know what that means or how to implement that or if it's related. I would think httpx would merge my ":path" header with the default one to eliminate the duplicate but I'm just spitballing here.
Full Traceback
File "/Users/User/.pyenv/versions/3.9.0/lib/python3.9/site-packages/httpx/_client.py", line 992, in post
return self.request(
File "/Users/User/.pyenv/versions/3.9.0/lib/python3.9/site-packages/httpx/_client.py", line 733, in request
return self.send(
File "/Users/User/.pyenv/versions/3.9.0/lib/python3.9/site-packages/httpx/_client.py", line 767, in send
response = self._send_handling_auth(
File "/Users/User/.pyenv/versions/3.9.0/lib/python3.9/site-packages/httpx/_client.py", line 805, in _send_handling_auth
response = self._send_handling_redirects(
File "/Users/User/.pyenv/versions/3.9.0/lib/python3.9/site-packages/httpx/_client.py", line 837, in _send_handling_redirects
response = self._send_single_request(request, timeout)
File "/Users/User/.pyenv/versions/3.9.0/lib/python3.9/site-packages/httpx/_client.py", line 861, in _send_single_request
(status_code, headers, stream, ext) = transport.request(
File "/Users/User/.pyenv/versions/3.9.0/lib/python3.9/site-packages/httpcore/_sync/connection_pool.py", line 218, in request
response = connection.request(
File "/Users/User/.pyenv/versions/3.9.0/lib/python3.9/site-packages/httpcore/_sync/connection.py", line 106, in request
return self.connection.request(method, url, headers, stream, ext)
File "/Users/User/.pyenv/versions/3.9.0/lib/python3.9/site-packages/httpcore/_sync/http2.py", line 119, in request
return h2_stream.request(method, url, headers, stream, ext)
File "/Users/User/.pyenv/versions/3.9.0/lib/python3.9/site-packages/httpcore/_sync/http2.py", line 292, in request
self.send_headers(method, url, headers, has_body, timeout)
File "/Users/User/.pyenv/versions/3.9.0/lib/python3.9/site-packages/httpcore/_sync/http2.py", line 330, in send_headers
self.connection.send_headers(self.stream_id, headers, end_stream, timeout)
File "/Users/User/.pyenv/versions/3.9.0/lib/python3.9/site-packages/httpcore/_sync/http2.py", line 227, in send_headers
self.h2_state.send_headers(stream_id, headers, end_stream=end_stream)
File "/Users/User/.pyenv/versions/3.9.0/lib/python3.9/site-packages/h2/connection.py", line 770, in send_headers
frames = stream.send_headers(
File "/Users/User/.pyenv/versions/3.9.0/lib/python3.9/site-packages/h2/stream.py", line 865, in send_headers
frames = self._build_headers_frames(
File "/Users/User/.pyenv/versions/3.9.0/lib/python3.9/site-packages/h2/stream.py", line 1252, in _build_headers_frames
encoded_headers = encoder.encode(headers)
File "/Users/User/.pyenv/versions/3.9.0/lib/python3.9/site-packages/hpack/hpack.py", line 249, in encode
for header in headers:
File "/Users/User/.pyenv/versions/3.9.0/lib/python3.9/site-packages/h2/utilities.py", line 496, in inner
for header in headers:
File "/Users/User/.pyenv/versions/3.9.0/lib/python3.9/site-packages/h2/utilities.py", line 441, in _validate_host_authority_header
for header in headers:
File "/Users/User/.pyenv/versions/3.9.0/lib/python3.9/site-packages/h2/utilities.py", line 338, in _reject_pseudo_header_fields
raise ProtocolError(
h2.exceptions.ProtocolError: Received duplicate pseudo-header field b':method'
Request:
devServer = "https://api.sandbox.push.apple.com:443"
title = "some title"
notification = { "aps": { "alert": title, "sound": "someSound.caf" } }
client = httpx.Client(http2=True)
try:
r = client.post(devServer, json=notification, headers=headers)
finally:
client.close()
Just need to append '/3/device/{}'.format(deviceToken) to the devServer url as the path, and the ":path" pseudo-header will be automatically set to it.
that is,
devServer = 'https://api.sandbox.push.apple.com:443/3/device/{}'.format(deviceToken)
Explanation:
The ":path", ":method" and ":scheme" pseudo-headers generally wouldn't need to be added manually in http2
Reference: Hypertext Transfer Protocol Version 2 (HTTP/2)

Having trouble uploading files with Box.com API

I am new to programming and learning python, so please bear with me, I appreciate the help....
I am working on a project where I need to upload files to storage services and I am currently trying to use the box API. I am trying to work with the code on this page:
how to use python's Request library to make an API call with an attachment and a parameter
import requests
import json
#the user access token
access_token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
#the name of the file as you want it to appear in box
filename = 'box_file'
#the actual file path
src_file = "C:\Python\Wildlife.wmv"
#the id of the folder you want to upload to
parent_id = '0'
headers = { 'Authorization: Bearer {0}'.format(access_token)}
url = 'https://upload.box.com/api/2.0/files/content'
files = { 'filename': (filename, open(src_file,'rb')) }
data = { "parent_id": parent_id }
response = requests.post(url, data, files, headers)
file_info = response.json()
I have tried a number of different things that really haven't gotten me any closer, so I am posting my slight adaptation of their code. Currently I am getting this error:
Traceback (most recent call last):
File "transfer2.py", line 18, in <module>
response = requests.post(url, data, files, headers)
TypeError: post() takes from 1 to 3 positional arguments but 4 were given
I have also had issues with the file_info = response.json()" in some of my other experiments. If someone could help me to get this working I would greatly appreciate it.
I am using python 3 if that helps.
edit 4/6
As requested, I changed this line:
response = requests.post(url, data=data, files=files, headers=headers)
This is the error I now get:
Traceback (most recent call last):
File "transfer2.py", line 18, in <module>
response = requests.post(url, data=data, files=files, headers=headers)
File "C:\Python34\lib\site-packages\requests\api.py", line 108, in post
return request('post', url, data=data, json=json, **kwargs)
File "C:\Python34\lib\site-packages\requests\api.py", line 50, in request
response = session.request(method=method, url=url, **kwargs)
File "C:\Python34\lib\site-packages\requests\sessions.py", line 450, in request
prep = self.prepare_request(req)
File "C:\Python34\lib\site-packages\requests\sessions.py", line 381, in prepare_request
hooks=merge_hooks(request.hooks, self.hooks),
File "C:\Python34\lib\site-packages\requests\models.py", line 305, in prepare
self.prepare_headers(headers)
File "C:\Python34\lib\site-packages\requests\models.py", line 410, in prepare_headers
self.headers = CaseInsensitiveDict((to_native_string(name), value) for name, value in headers.items())
AttributeError: 'set' object has no attribute 'items'
In the requests library for request.post(), headers and files are both keyword arguments only, I would also make data a keyword argument, e.g.:
response = requests.post(url, data=data, files=files, headers=headers)
from boxsdk import Client, OAuth2
oauth = OAuth2( client_id="dlpjkcxxxxxxxxxxxxxxxxxxxxcom",client_secret="xxxxxxxxxxxxxxxxxxxxxxxxx", access_token="xxxxxxxxxxxxxxxxxxxxxxxxxxx", )
client = Client(oauth)
shared_folder = client.folder(folder_id='0',).create_subfolder('sxxxx')
uploaded_file = shared_folder.upload('/root/xxxxxx.txt')

Can't POST file attachment and data parameters with Python requests lib

I'm trying to implement a simple Django view that accept a file attachment and some other parameters and proxy the request on a remote API call.
Please note: the proxy is not the point of my question :)
This is how I implemented the view:
def image_upload(request):
token = request.POST['token']
image_file = request.FILES.values()[0]
files = {'file': ('myupload.txt', image_file.read())}
client_id = request.POST['client_id']
folder_id = request.POST['folder_id']
advert_id = request.POST['advert_id']
image_type = request.POST['image_type']
crop_image = request.POST['crop_image']
api_base_url = settings.API_BASE_URL
file_post_data = {'client_id': client_id, 'folder_id': folder_id, 'advert_id': advert_id,
'image_type': image_type, 'crop_image': crop_image}
auth_header = {'Authorization': 'Token ' + token}
r = requests.post(api_base_url + 'assets/image/upload/',
data = json.dumps(file_post_data),
headers = auth_header,
files = files)
return r.json()
The problem is that when I test this view (I use Django Test Client to do it) I get an error on the line "files = files)" that says "ValueError: cannot encode objects that are not 2-tuples".
The complete trace log is this one:
======================================================================
ERROR: test_image_upload (fbx.tests.FbxTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/andrea/Documents/src/fbxapp/onboard/fbx/tests.py", line 18, in test_image_upload
'image_type': 'A', 'crop_image': False, 'attachment': fp})
File "/usr/local/lib/python2.7/dist-packages/django/test/client.py", line 449, in post
response = super(Client, self).post(path, data=data, content_type=content_type, **extra)
File "/usr/local/lib/python2.7/dist-packages/django/test/client.py", line 262, in post
return self.request(**r)
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py", line 111, in get_response
response = callback(request, *callback_args, **callback_kwargs)
File "/home/andrea/Documents/src/fbxapp/onboard/fbx/views.py", line 42, in image_upload
files = files)
File "/usr/local/lib/python2.7/dist-packages/requests/api.py", line 88, in post
return request('post', url, data=data, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/requests/api.py", line 44, in request
return session.request(method=method, url=url, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 324, in request
prep = req.prepare()
File "/usr/local/lib/python2.7/dist-packages/requests/models.py", line 225, in prepare
p.prepare_body(self.data, self.files)
File "/usr/local/lib/python2.7/dist-packages/requests/models.py", line 385, in prepare_body
(body, content_type) = self._encode_files(files, data)
File "/usr/local/lib/python2.7/dist-packages/requests/models.py", line 99, in _encode_files
fields = to_key_val_list(data or {})
File "/usr/local/lib/python2.7/dist-packages/requests/utils.py", line 136, in to_key_val_list
raise ValueError('cannot encode objects that are not 2-tuples')
ValueError: cannot encode objects that are not 2-tuples
I've also tried a quick test using this to read a file: files = {'file': ('myupload.txt', open('/tmp/mytmp.txt', 'rb'))}
but it doesn't work. Do tou have any idea about how to fix this?
Thanks!
You cannot simultaneously post JSON data and multipart/form-data which is in essence what you're trying to do here. json.dumps returns a string so you're sending a string that looks like
'{"client_id": 1, "folder_id": 2, "advert_id": 3, "image_type": "jpeg", "crop_image": true}'
And then telling requests you want to use that in combination with a multipart/form-data request by sending something in via the files parameter. That is impossible and could possibly raise a better exception.
Either add the file to the JSON data after reading it into memory and send that with the appropriate Content-Type header or send the entire thing as a multipart/form-data request without using json.dumps and simply passing the dictionary you're creating to data. Use one or the other but not both.
That aside, you say that your exception comes from the line that only has files=files) on it, but it is not that line alone that causes the exception. The exception is raised by the function which happens to end on that line. The fact that your exception arises from that too is mere coincidence. This is an wart in Python that may possibly be fixed in Python 3.4. You should upgrade, because 3.4 will be awesome and newer versions of Django support Python 3.x.

Python Extracting binary from a POST request using web.py

I am developing an API that allows outside clients to send a binary file which will be processed. my web.data() is a string and the function I am calling requires a binary. How do I get it into the correct format? Maybe I have the incorrect Headers? how do I extract the binary data. I am using web.py.
-----------------POST request----------------------------------------------------
import json
import requests
files = {'file':('000038fe4b46c210c37bdde767835007', open('000038fe4b46c210c37bdde767835007', 'rb'))}
headers = {'content-type' : 'application/octet-stream', 'X-Auth-Token':'xxxf'}
r = requests.post('http://XXX:8080/v1/binaries', files = files, headers = header
-----------------------API function------------------------------
def POST(self):
a = web.ctx.env.get("HTTP_X_AUTH_TOKEN", None)
creds = authenticator(a)
postdata = web.data().read()
analysis = atklite.FileAnalysis(data=postdata)
metadata = analysis.return_analysis()
------------------------Traceback--------------------------------
File "/usr/lib/pymodules/python2.7/web/application.py", line 242, in process
return self.handle()
File "/usr/lib/pymodules/python2.7/web/application.py", line 233, in handle
return self._delegate(fn, self.fvars, args)
File "/usr/lib/pymodules/python2.7/web/application.py", line 415, in _delegate
return handle_class(cls)
File "/usr/lib/pymodules/python2.7/web/application.py", line 390, in handle_class
return tocall(*args)
File "/home/XXXXXX/ProcessingCode/bfsapi.py", line 75, in POST
postdata = web.data().read()
AttributeError: 'str' object has no attribute 'read'
Thanks
Sorry if the formatting got all messed up in the Post.
Even if it is a binary file, reading raw post data would get you a encoded string. You would need to decode to convert to binary data. You can write to a file as follows:
written = open('binary.file', 'wb')
for chunk in rawdata.chunks():
written.write(chunk)
written.close()

adding triples in 4store

here url_add is a link that contains the rdf triples that i want to store in 4store.but if i pass url_add as an argument it generates Relative URIerror .
so what is the way in which i can pass url_add as an argument only.
response = store.add_from_uri('url_add')
Traceback (most recent call last):
File "", line 1, in
File "/usr/local/lib/python2.7/dist-packages/django_gstudio-0.3.dev-py2.7.egg/gstudio/testing1.py", line 152, in
response = store.add_from_uri('url_add')
File "/usr/local/lib/python2.7/dist-packages/django_gstudio-0.3.dev-py2.7.egg/gstudio/HTTP4Store/HTTP4Store.py", line 74, in add_from_uri
r_obj = self.rh.GET(uri, headers=headers)
File "/usr/local/lib/python2.7/dist-packages/django_gstudio-0.3.dev-py2.7.egg/gstudio/HTTP4Store/utils.py", line 53, in GET
return self._request("%s" % (path), method="GET", headers=headers)
File "/usr/local/lib/python2.7/dist-packages/django_gstudio-0.3.dev-py2.7.egg/gstudio/HTTP4Store/utils.py", line 92, in _request
resp, content = self.h.request(path, method, headers=headers, body=data)
File "/usr/lib/python2.7/dist-packages/httplib2/init.py", line 1297, in request
(scheme, authority, request_uri, defrag_uri) = urlnorm(uri)
File "/usr/lib/python2.7/dist-packages/httplib2/init.py", line 204, in urlnorm
raise RelativeURIError("Only absolute URIs are allowed. uri = %s" % uri)
RelativeURIError: Only absolute URIs are allowed. uri = url_add
What is the value of your URL
It is throwing an exception because you are passing a relative url instead of an absolute (you probably have something like "../../directory/filename.rdf"
If your url is an HTTP url e.g (http://host/filename.rdf) and dereferenceable then you can also use the LOAD directive as part of SPARQL update. So that simply means exectuting a SPARQL query (in the same way you execute a SPARQL query using 4store) using the following expression:
LOAD <http://host/filename.rdf>
INTO GRAPH <http://optional-name-of-graph>

Categories