Out of Memory with MultiPart Post, Python 2.7 - python

I have a 300 mb file that I need to upload, and my current code just isn't cutting it.
#----------------------------------------------------------------------------------
def _post_multipart(self, host, selector,
fields, files,
ssl=False,port=80,
proxy_url=None,proxy_port=None):
""" performs a multi-post to AGOL, Portal, or AGS
Inputs:
host - string - root url (no http:// or https://)
ex: www.arcgis.com
selector - string - everything after the host
ex: /PWJUSsdoJDp7SgLj/arcgis/rest/services/GridIndexFeatures/FeatureServer/0/1/addAttachment
fields - dictionary - additional parameters like token and format information
files - tuple array- tuple with the file name type, filename, full path
ssl - option to use SSL
proxy_url - string - url to proxy server
proxy_port - interger - port value if not on port 80
Output:
JSON response as dictionary
Useage:
import urlparse
url = "http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/SanFrancisco/311Incidents/FeatureServer/0/10261291"
parsed_url = urlparse.urlparse(url)
params = {"f":"json"}
print _post_multipart(host=parsed_url.hostname,
selector=parsed_url.path,
files=files,
fields=params
)
"""
content_type, body = self._encode_multipart_formdata(fields, files)
headers = {
'content-type': content_type,
'content-length': str(len(body))
}
if proxy_url:
if ssl:
h = httplib.HTTPSConnection(proxy_url, proxy_port)
h.request('POST', 'https://' + host + selector, body, headers)
else:
h = httplib.HTTPConnection(proxy_url, proxy_port)
h.request('POST', 'http://' + host + selector, body, headers)
else:
if ssl:
h = httplib.HTTPSConnection(host,port)
h.request('POST', selector, body, headers)
else:
h = httplib.HTTPConnection(host,port)
h.request('POST', selector, body, headers)
resp_data = h.getresponse().read()
try:
result = json.loads(resp_data)
except:
return None
if 'error' in result:
if result['error']['message'] == 'Request not made over ssl':
return self._post_multipart(host=host, selector=selector, fields=fields,
files=files, ssl=True,port=port,
proxy_url=proxy_url,proxy_port=proxy_port)
return result
def _encode_multipart_formdata(self, fields, files):
boundary = mimetools.choose_boundary()
buf = StringIO()
for (key, value) in fields.iteritems():
buf.write('--%s\r\n' % boundary)
buf.write('Content-Disposition: form-data; name="%s"' % key)
buf.write('\r\n\r\n' + self._tostr(value) + '\r\n')
for (key, filepath, filename) in files:
if os.path.isfile(filepath):
buf.write('--%s\r\n' % boundary)
buf.write('Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename))
buf.write('Content-Type: %s\r\n' % (self._get_content_type3(filename)))
file = open(filepath, "rb")
try:
buf.write('\r\n' + file.read() + '\r\n')
finally:
file.close()
buf.write('--' + boundary + '--\r\n\r\n')
buf = buf.getvalue()
content_type = 'multipart/form-data; boundary=%s' % boundary
return content_type, buf
I cannot use requests module, and must use the standard libraries like urllib2, urllib, etc.. for python 2.7.x.
Is there a way to load the 300 mb files to a site without pushing the whole thing to memory?
UPDATE:
So I switched to requests, and now I get: MissingSchema: Invalid URL u'www.arcgis.com/sharing/rest/content/users//addItem?': No schema supplied. Perhaps you meant http://www.arcgis.com/sharing/rest/content/users//addItem??
What does this mean?
I provide the fields with the request.post() as such:
#----------------------------------------------------------------------------------
def _post_big_files(self, host, selector,
fields, files,
ssl=False,port=80,
proxy_url=None,proxy_port=None):
import sys
sys.path.insert(1,os.path.dirname(__file__))
from requests_toolbelt import MultipartEncoder
import requests
if proxy_url is not None:
proxyDict = {
"http" : "%s:%s" % (proxy_url, proxy_port),
"https" : "%s:%s" % (proxy_url, proxy_port)
}
else:
proxyDict = {}
for k,v in fields.iteritems():
print k,v
fields[k] = json.dumps(v)
for key, filepath, filename in files:
fields[key] = ('filename', open(filepath, 'rb'), self._get_content_type3(filepath))
m = MultipartEncoder(
fields=fields)
print host + selector
r = requests.post(host + selector , data=m,
headers={'Content-Type': m.content_type})
print r
I followed the example in the help documentation from both request and the toolbelt. Any ideas why this is breaking?
Thank you,

Related

Is there any way to repair this error with POST data in python?

I need to check the compatibility of the GPS device communication protocol with the logging server. The application allows you to perform a test that compares the sent frame with the protocols it supports and checks whether it is possible to read this frame by already supported protocols. For this purpose, I wanted to use the shared test, but I get an error.
https://github.com/traccar/traccar/blob/master/tools/test-integration.py
```
import sys
import os
import xml.etree.ElementTree
import urllib
import urllib.parse
import urllib.request as urllib2
import json
import socket
import time
messages = {
'gps103' : 'imei:123456789012345,help me,1201011201,,F,120100.000,A,6000.0000,N,13000.0000,E,0.00,;'
}
baseUrl = 'http://172.16.43.210:8082'
user = { 'email' : 'admin', 'password' : 'admin' }
debug = '-v' in sys.argv
def load_ports():
ports = {}
dir = os.path.dirname(os.path.abspath(__file__))
root = xml.etree.ElementTree.parse(dir + '\\default.xml').getroot()
for entry in root.findall('entry'):
key = entry.attrib['key']
if key.endswith('.port'):
ports[key[:-5]] = int(entry.text)
if debug:
print('\nports: %s\n' % repr(ports))
return ports
def login():
request = urllib2.Request(baseUrl + '/api/session')
response = urllib2.urlopen(request, urllib.parse.urlencode(user))
if debug:
print('\nlogin: %s\n' % repr(json.load(response)))
return response.headers.get('Set-Cookie')
def remove_devices(cookie):
request = urllib2.Request(baseUrl + '/api/devices')
request.add_header('Cookie', cookie)
response = urllib2.urlopen(request)
data = json.load(response)
if debug:
print ('\ndevices: %s\n' % repr(data))
for device in data:
request = urllib2.Request(baseUrl + '/api/devices/' + str(device['id']))
request.add_header('Cookie', cookie)
request.get_method = lambda: 'DELETE'
response = urllib2.urlopen(request)
def add_device(cookie, unique_id):
request = urllib2.Request(baseUrl + '/api/devices')
request.add_header('Cookie', cookie)
request.add_header('Content-Type', 'application/json')
device = { 'name' : unique_id, 'uniqueId' : unique_id }
response = urllib2.urlopen(request, json.dumps(device))
data = json.load(response)
return data['id']
def send_message(port, message):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', port))
s.send(message)
s.close()
def get_protocols(cookie, device_id):
params = { 'deviceId' : device_id, 'from' : '2000-01-01T00:00:00.000Z', 'to' : '2050-01-01T00:00:00.000Z' }
request = urllib2.Request(baseUrl + '/api/positions?' + urllib.urlencode(params))
request.add_header('Cookie', cookie)
request.add_header('Content-Type', 'application/json')
request.add_header('Accept', 'application/json')
response = urllib2.urlopen(request)
protocols = []
for position in json.load(response):
protocols.append(position['protocol'])
return protocols
ports = load_ports()
cookie = login()
remove_devices(cookie)
devices = {
'123456789012345' : add_device(cookie, '123456789012345'),
'123456789012' : add_device(cookie, '123456789012'),
'1234567890' : add_device(cookie, '1234567890'),
'123456' : add_device(cookie, '123456'),
'1234' : add_device(cookie, '1234')
}
all = set(ports.keys())
protocols = set(messages.keys())
print ('Total: %d' % len(all))
print ('Missing: %d' % len(all - protocols))
print ('Covered: %d' % len(protocols))
#if all - protocols:
# print '\nMissing: %s\n' % repr(list((all - protocols)))
for protocol in messages:
send_message(ports[protocol], messages[protocol])
time.sleep(10)
for device in devices:
protocols -= set(get_protocols(cookie, devices[device]))
print ('Success: %d' % (len(messages) - len(protocols)))
print ('Failed: %d' % len(protocols))
if protocols:
print ('\nFailed: %s' % repr(list(protocols)))
```
And i got this error:
File "C:\Users\ISUIT\Desktop\Ew.Prac\tt.py", line 159, in <module>
cookie = login()
File "C:\Users\ISUIT\Desktop\Ew.Prac\tt.py", line 112, in login
response = urllib2.urlopen(request, urllib.parse.urlencode(user))
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.1776.0_x64__qbz5n2kfra8p0\lib\urllib\request.py", line 214, in urlopen
return opener.open(url, data, timeout)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.1776.0_x64__qbz5n2kfra8p0\lib\urllib\request.py", line 514, in open
req = meth(req)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.1776.0_x64__qbz5n2kfra8p0\lib\urllib\request.py", line 1277, in do_request_
raise TypeError(msg)
TypeError: POST data should be bytes, an iterable of bytes, or a file object. It cannot be of type str.
I try to use bytes(string, 'utf-8') but it doesn't work
Use string.encode('utf-8') to cast the type to bytes.

unable to add image binary into multipart/form-data

I received an error: expected str instance, bytes found when i tried to add image binary into multipart/form-data.
The problem is i tried to append the imageData in binary format to strings. Is there a way to add binary image to multipart/form data?
I'm at my wits end, would appreciate some help for this.
imageData = request.FILES['filePath'].read()
content_type, request_body = encode_multipart_formdata([('include_target_data', targetMetaDataRet),
('max_num_results', str(maxNoResultRet))],
[('image', imagePath, imageData)])
def encode_multipart_formdata(fields, files):
BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$'
CRLF = '\r\n'
lines = []
for (key, value) in fields:
lines.append('--' + BOUNDARY)
lines.append('Content-Disposition: form-data; name="%s"' % key)
lines.append('')
lines.append(value)
for (key, filename, value) in files:
lines.append('--' + BOUNDARY)
lines.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
lines.append('Content-Type: %s' % get_content_type(filename))
lines.append('')
lines.append(value)
lines.append('--' + BOUNDARY + '--')
lines.append('')
body = CRLF.join(lines)
content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
return content_type, body
Traceback:
35. response = get_response(request)
128. response = self.process_exception_by_middleware(e, request)
126. response = wrapped_callback(request, *callback_args, **callback_kwargs)
166. [('image', imagePath, imageData)])
232. body = CRLF.join(lines)
Exception Type: TypeError at /identify_shrine
Exception Value: sequence item 12: expected str instance, bytes found
as per #coltoneakins request I modified request body to bytes, but I seem to be getting a bad request error any idea why?
Code:
content_type = 'multipart/form-data; boundary=----------ThIs_Is_tHe_bouNdaRY_$'
request_body = '----------ThIs_Is_tHe_bouNdaRY_$' + '\n'+'Content-Disposition: form-data; name="include_target_data"' + '\n' + '\n' + 'top'+ '\n' + '----------ThIs_Is_tHe_bouNdaRY_$' +'\n' + 'Content-Disposition: form-data; name="max_num_results"' + '\n' + '\n' + '1' + '\n' + '----------ThIs_Is_tHe_bouNdaRY_$' +'\n' + 'Content-Disposition: form-data; name="image"; filename="img_2.jpg"' + '\n' + 'Content-Type: image/jpeg' + '\n' + '\n'
request_body1 = request_body.encode('utf-8')
request_body2 = imageData
request_body3 = ('\n' + '\n' + '----------ThIs_Is_tHe_bouNdaRY_$').encode('utf-8')
request_body4 = request_body1 + request_body2 + request_body3
content_type_bare = 'multipart/form-data'
# Sign the request and get the Authorization header
# use client key
auth_header = authorization_header_for_request(CLIENT_ACCESS_KEY, CLIENT_SECRET_KEY, HTTP_METHOD, request_body4,
content_type_bare,
date, path)
request_headers = {
'Accept': 'application/json',
'Authorization': auth_header,
'Content-Type': content_type,
'Date': date
}
try:
# Make the request over HTTPS on port 443
connection = http.client.HTTPSConnection(CLOUD_RECO_API_ENDPOINT, 443)
connection.request(HTTP_METHOD, path, request_body4, request_headers)
response = connection.getresponse()
response_body = response.read()
reason = response.reason
status = response.status
finally:
connection.close()
You have a type issue in your code. You are getting a TypeError expected str instance, bytes found because you are attempting to join() a list that contains both str types and bytes types in Python.
Look at these lines in your code:
for (key, filename, value) in files:
lines.append('--' + BOUNDARY)
lines.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
lines.append('Content-Type: %s' % get_content_type(filename))
lines.append('')
lines.append(value) # <---------- THIS IS BYTES, EVERYTHING ELSE IS STR
lines.append('--' + BOUNDARY + '--')
lines.append('')
body = CRLF.join(lines) # <---------- AHHH RED FLAG!!!
CRLF is type str. But, value (which is added on to your lines list) is bytes. This means you end up with lines containing both str and bytes types. When you are sending an image via the mulitpart/form-data request, the whole body of the request is bytes. So, you need to use join() with only bytes types.
This is what you are doing:
body = CRLF.join(lines)
which is really:
'\r\n, i am a str'.join(['i am also str', b'I am not a str, I am bytes']) # <-- NO
This is what you need to be doing:
b'I am bytes'.join([b'I am also bytes', b'Me too!'])
Also, just so that you are aware, the Requests library provides mechanisms for you to send files. See the files parameter in the Requests documentation or this StackOverflow answer:
https://stackoverflow.com/a/12385661/9347694
So, you may not need to reinvent the wheel here. Requests will multipart encode the file and construct the request for you.

Python - Capture Individaul value result of request

I am new to Python. I am trying to capture individual values that returned from the request result.
Here is part of my code to send the request:
opener = urllib2.build_opener(urllib2.HTTPHandler)
request = urllib2.Request(url, data=json_payload)
auth = base64.encodestring('%s:%s' % (user, pwd)).replace('\n', '')
request.add_header('Authorization', 'Basic %s' % auth)
request.add_header('Content-Type', 'application/json')
request.add_header('Accept', 'application/json')
request.get_method = lambda: 'POST'
# Perform the request
result = opener.open(request).read()
print result
The print result gives me the output below (messy formatting)
{"#odata.context":"https://outlook.office365.com/api/v1.0/$metadata#Me/Events(Start,End)/$entity","#odata.id":"https://outlook.office365.com
/api/v1.0/Users('user1#domain.com')/Events('AAMkADA1OWVjOTkxLTlmYmEtNDAwMS04YWU3LTNkNYDHE4YjU2OGI1ZABGBBBBBBD_fa49_h8OTJ5eGdjSTEF3BwBOcC
SV9aNzSoXurwI4R0IgBBBBBBBENAABOcCSV9aNzSoXurwI4R0IgAAHn0Cy0AAA=')","#odata.etag":"W/\"TnAklfWjc0qF7q8COEdDTHDAB5+uOdw==\"","Id":"AAMkADA1OWVjO
TkxLTlmYmEtNDAwMS04YWU3LTMHKNDE2YjU2OGI1ZABGAAAAAAD_fa49_h8OTJ5eGdjSTEF3BwBOcCSV9aNzSoXurwI4R0IgCCCCCCCENAABOcCSV9aNzSoXurwI4R0IgAAHn0Cy0AAA="
,"Start":"2016-08-13T15:00:00-07:00","End":"2016-08-13T16:00:00-07:00"}
Is there a way that I load the result into json.load and capture individual values for #odata.context, #odata.id, #odata.etag, Id, Start, End?
I tried this, but no luck.
data = json.load(result)
print data["#odata.context"]
Have you tried using json loads. load in the background uses the method .read() so if you wanted to use it you must use a file like object like StringIO or file/open
Like this:
import urllib2
import json
import base64
test_load = "foo=bar"
user = "test"
pwd = "test"
opener = urllib2.build_opener(urllib2.HTTPHandler)
request = urllib2.Request("http://scooterlabs.com/echo.json?%s" % test_load, data=b'data')
auth = base64.encodestring('%s:%s' % (user, pwd)).replace('\n', '')
request.add_header('Authorization', 'Basic %s' % auth)
request.add_header('Content-Type', 'application/json')
request.add_header('Accept', 'application/json')
request.get_method = lambda: 'POST'
result = opener.open(request).read()
jsons = json.loads(result)
print("FULL: %s" % result)
print(jsons[u'method'])
print(jsons[u'request'])
Edit:
It seems as if the comments already answered the question... sorry

Multipart form post to google app engine not working

I am trying to post a multi-part form using httplib, url is hosted on google app engine, on post it says Method not allowed, though the post using urllib2 works. Full working example is attached.
My question is what is the difference between two, why one works but not the other
is there a problem in my mulipart form post code?
or the problem is with google app engine?
or something else ?
import httplib
import urllib2, urllib
# multipart form post using httplib fails, saying
# 405, 'Method Not Allowed'
url = "http://mockpublish.appspot.com/publish/api/revision_screen_create"
_, host, selector, _, _ = urllib2.urlparse.urlsplit(url)
print host, selector
h = httplib.HTTP(host)
h.putrequest('POST', selector)
BOUNDARY = '----------THE_FORM_BOUNDARY'
content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
h.putheader('content-type', content_type)
h.putheader('User-Agent', 'Python-urllib/2.5,gzip(gfe)')
content = ""
L = []
L.append('--' + BOUNDARY)
L.append('Content-Disposition: form-data; name="test"')
L.append('')
L.append("xxx")
L.append('--' + BOUNDARY + '--')
L.append('')
content = '\r\n'.join(L)
h.putheader('content-length', str(len(content)))
h.endheaders()
h.send(content)
print h.getreply()
# post using urllib2 works
data = urllib.urlencode({'test':'xxx'})
request = urllib2.Request(url)
f = urllib2.urlopen(request, data)
output = f.read()
print output
Edit: After changing putrequest to request (on Nick Johnson's suggestion), it works
url = "http://mockpublish.appspot.com/publish/api/revision_screen_create"
_, host, selector, _, _ = urllib2.urlparse.urlsplit(url)
h = httplib.HTTPConnection(host)
BOUNDARY = '----------THE_FORM_BOUNDARY'
content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
content = ""
L = []
L.append('--' + BOUNDARY)
L.append('Content-Disposition: form-data; name="test"')
L.append('')
L.append("xxx")
L.append('--' + BOUNDARY + '--')
L.append('')
content = '\r\n'.join(L)
h.request('POST', selector, content,{'content-type':content_type})
res = h.getresponse()
print res.status, res.reason, res.read()
so now the question remains what is the difference between two approaches and can first first be made to work?
Nick Johnson's answer
Have you tried sending the request with httplib using .request() instead of .putrequest() etc, supplying the headers as a dict?
it works!

Send file using POST from a Python script

Is there a way to send a file using POST from a Python script?
From: https://requests.readthedocs.io/en/latest/user/quickstart/#post-a-multipart-encoded-file
Requests makes it very simple to upload Multipart-encoded files:
with open('report.xls', 'rb') as f:
r = requests.post('http://httpbin.org/post', files={'report.xls': f})
That's it. I'm not joking - this is one line of code. The file was sent. Let's check:
>>> r.text
{
"origin": "179.13.100.4",
"files": {
"report.xls": "<censored...binary...data>"
},
"form": {},
"url": "http://httpbin.org/post",
"args": {},
"headers": {
"Content-Length": "3196",
"Accept-Encoding": "identity, deflate, compress, gzip",
"Accept": "*/*",
"User-Agent": "python-requests/0.8.0",
"Host": "httpbin.org:80",
"Content-Type": "multipart/form-data; boundary=127.0.0.1.502.21746.1321131593.786.1"
},
"data": ""
}
Yes. You'd use the urllib2 module, and encode using the multipart/form-data content type. Here is some sample code to get you started -- it's a bit more than just file uploading, but you should be able to read through it and see how it works:
user_agent = "image uploader"
default_message = "Image $current of $total"
import logging
import os
from os.path import abspath, isabs, isdir, isfile, join
import random
import string
import sys
import mimetypes
import urllib2
import httplib
import time
import re
def random_string (length):
return ''.join (random.choice (string.letters) for ii in range (length + 1))
def encode_multipart_data (data, files):
boundary = random_string (30)
def get_content_type (filename):
return mimetypes.guess_type (filename)[0] or 'application/octet-stream'
def encode_field (field_name):
return ('--' + boundary,
'Content-Disposition: form-data; name="%s"' % field_name,
'', str (data [field_name]))
def encode_file (field_name):
filename = files [field_name]
return ('--' + boundary,
'Content-Disposition: form-data; name="%s"; filename="%s"' % (field_name, filename),
'Content-Type: %s' % get_content_type(filename),
'', open (filename, 'rb').read ())
lines = []
for name in data:
lines.extend (encode_field (name))
for name in files:
lines.extend (encode_file (name))
lines.extend (('--%s--' % boundary, ''))
body = '\r\n'.join (lines)
headers = {'content-type': 'multipart/form-data; boundary=' + boundary,
'content-length': str (len (body))}
return body, headers
def send_post (url, data, files):
req = urllib2.Request (url)
connection = httplib.HTTPConnection (req.get_host ())
connection.request ('POST', req.get_selector (),
*encode_multipart_data (data, files))
response = connection.getresponse ()
logging.debug ('response = %s', response.read ())
logging.debug ('Code: %s %s', response.status, response.reason)
def make_upload_file (server, thread, delay = 15, message = None,
username = None, email = None, password = None):
delay = max (int (delay or '0'), 15)
def upload_file (path, current, total):
assert isabs (path)
assert isfile (path)
logging.debug ('Uploading %r to %r', path, server)
message_template = string.Template (message or default_message)
data = {'MAX_FILE_SIZE': '3145728',
'sub': '',
'mode': 'regist',
'com': message_template.safe_substitute (current = current, total = total),
'resto': thread,
'name': username or '',
'email': email or '',
'pwd': password or random_string (20),}
files = {'upfile': path}
send_post (server, data, files)
logging.info ('Uploaded %r', path)
rand_delay = random.randint (delay, delay + 5)
logging.debug ('Sleeping for %.2f seconds------------------------------\n\n', rand_delay)
time.sleep (rand_delay)
return upload_file
def upload_directory (path, upload_file):
assert isabs (path)
assert isdir (path)
matching_filenames = []
file_matcher = re.compile (r'\.(?:jpe?g|gif|png)$', re.IGNORECASE)
for dirpath, dirnames, filenames in os.walk (path):
for name in filenames:
file_path = join (dirpath, name)
logging.debug ('Testing file_path %r', file_path)
if file_matcher.search (file_path):
matching_filenames.append (file_path)
else:
logging.info ('Ignoring non-image file %r', path)
total_count = len (matching_filenames)
for index, file_path in enumerate (matching_filenames):
upload_file (file_path, index + 1, total_count)
def run_upload (options, paths):
upload_file = make_upload_file (**options)
for arg in paths:
path = abspath (arg)
if isdir (path):
upload_directory (path, upload_file)
elif isfile (path):
upload_file (path)
else:
logging.error ('No such path: %r' % path)
logging.info ('Done!')
Looks like python requests does not handle extremely large multi-part files.
The documentation recommends you look into requests-toolbelt.
Here's the pertinent page from their documentation.
The only thing that stops you from using urlopen directly on a file object is the fact that the builtin file object lacks a len definition. A simple way is to create a subclass, which provides urlopen with the correct file.
I have also modified the Content-Type header in the file below.
import os
import urllib2
class EnhancedFile(file):
def __init__(self, *args, **keyws):
file.__init__(self, *args, **keyws)
def __len__(self):
return int(os.fstat(self.fileno())[6])
theFile = EnhancedFile('a.xml', 'r')
theUrl = "http://example.com/abcde"
theHeaders= {'Content-Type': 'text/xml'}
theRequest = urllib2.Request(theUrl, theFile, theHeaders)
response = urllib2.urlopen(theRequest)
theFile.close()
for line in response:
print line
Chris Atlee's poster library works really well for this (particularly the convenience function poster.encode.multipart_encode()). As a bonus, it supports streaming of large files without loading an entire file into memory. See also Python issue 3244.
I am trying to test django rest api and its working for me:
def test_upload_file(self):
filename = "/Users/Ranvijay/tests/test_price_matrix.csv"
data = {'file': open(filename, 'rb')}
client = APIClient()
# client.credentials(HTTP_AUTHORIZATION='Token ' + token.key)
response = client.post(reverse('price-matrix-csv'), data, format='multipart')
print response
self.assertEqual(response.status_code, status.HTTP_200_OK)
pip install http_file
#импорт вспомогательных библиотек
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
import requests
#импорт http_file
from http_file import download_file
#создание новой сессии
s = requests.Session()
#соеденение с сервером через созданную сессию
s.get('URL_MAIN', verify=False)
#загрузка файла в 'local_filename' из 'fileUrl' через созданную сессию
download_file('local_filename', 'fileUrl', s)
You may also want to have a look at httplib2, with examples. I find using httplib2 is more concise than using the built-in HTTP modules.
def visit_v2(device_code, camera_code):
image1 = MultipartParam.from_file("files", "/home/yuzx/1.txt")
image2 = MultipartParam.from_file("files", "/home/yuzx/2.txt")
datagen, headers = multipart_encode([('device_code', device_code), ('position', 3), ('person_data', person_data), image1, image2])
print "".join(datagen)
if server_port == 80:
port_str = ""
else:
port_str = ":%s" % (server_port,)
url_str = "http://" + server_ip + port_str + "/adopen/device/visit_v2"
headers['nothing'] = 'nothing'
request = urllib2.Request(url_str, datagen, headers)
try:
response = urllib2.urlopen(request)
resp = response.read()
print "http_status =", response.code
result = json.loads(resp)
print resp
return result
except urllib2.HTTPError, e:
print "http_status =", e.code
print e.read()
I tried some of the options here, but I had some issue with the headers ('files' field was empty).
A simple mock to explain how I did the post using requests and fixing the issues:
import requests
url = 'http://127.0.0.1:54321/upload'
file_to_send = '25893538.pdf'
files = {'file': (file_to_send,
open(file_to_send, 'rb'),
'application/pdf',
{'Expires': '0'})}
reply = requests.post(url=url, files=files)
print(reply.text)
More at https://requests.readthedocs.io/en/latest/user/quickstart/
To test this code, you could use a simple dummy server as this one (thought to run in a GNU/Linux or similar):
import os
from flask import Flask, request, render_template
rx_file_listener = Flask(__name__)
files_store = "/tmp"
#rx_file_listener.route("/upload", methods=['POST'])
def upload_file():
storage = os.path.join(files_store, "uploaded/")
print(storage)
if not os.path.isdir(storage):
os.mkdir(storage)
try:
for file_rx in request.files.getlist("file"):
name = file_rx.filename
destination = "/".join([storage, name])
file_rx.save(destination)
return "200"
except Exception:
return "500"
if __name__ == "__main__":
rx_file_listener.run(port=54321, debug=True)

Categories