send both string and buffer using httplib with python to the server
Question
How can I POST parameters with a file object to a URL using httplib in python web services.
Am using the following scripts:
import httplib
import urllib
params = urllib.urlencode({"@str1":"string1", "@str2":"string2", "@file":"/local/path/to/file/in/client/machine", "@action":"action.php" })
headers = {"Content-type":"application/pdf , text/*" }
conn = httplib.HTTPConnection("192.168.2.17")
conn.request("POST", "/SomeName/action.php", params, headers)
response = conn.getresponse()
print response.status, response.reason
data = response.read()
data
conn.close()
And I have the following output:
200
OK
<html>.....some html code </html>
I wrote some php code for save those string and the file in DB My problem is that, Am only getting the file path as a sting but not my file. May be I have to send the file object like,
file_obj = open("filename.txt","r")
conn.request("POST", "/SomeName/action.php", file_obj, headers)
But I want to send both strings and file. Any suggestions to solve this?
EDIT I change my code as follows: When i send a pdf file, by directly using httplib, to my server the file saves as BIN file.
def document_management_service(self,str_loc_file_path,*args):
locfile = open(str_loc_file_path,'rb').read()
host = "some.hostname.com"
selector = "/api/?task=create"
fields = [('docName','INVOICE'),('docNumber','DOC/3'),('cusName','name'),('cusNumber','C124'),('category','INVOICE'),('data','TIJO MRS,SOME/DATA/CONTENT,Blahblah,2584.00,blahblah'),('action','create')]
files = [('strfile','File.pdf',locfile)]
response = self.post_multipart(host, selector, fields, files)
print response
pass
def post_multipart(self,host, selector, fields, files):
content_type, body = self.encode_multipart_formdata(fields, files)
h = httplib.HTTP(host)
h.set_debuglevel(1)
h.putrequest('POST', selector)
h.putheader('content-type', content_type)
h.putheader('content-length', str(len(body)))
h.putheader('Host', host)
h.endheaders()
h.send(body)
errcode, errmsg, headers= h.getreply()
return h.file.read()
def encode_multipart_formdata(self, fields, files):
LIMIT = '----------lImIt_of_THE_fIle_eW_$'
CRLF = '\r\n'
L = []
for (key, value) in fields:
L.append('--' + LIMIT)
L.append('Content-Disposition: form-data; name="%s"' % key)
L.append('')
L.append(value)
for (key, filename, value) in files:
L.append('--' + LIMIT)
L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
L.append('Content-Type: %s' % self.get_content_type(filename))
L.append('')
L.append(value)
L.append('--' + LIMIT + '--')
L.append('')
body = CRLF.join(L)
content_type = 'multipart/form-data; boundary=%s' % LIMIT
return content_type, body
def get_content_type(self, filename):
return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
I have debug the request which shows as:
[('content-length', '4191'), ('accept-ranges', 'bytes'), ('server', 'Apache/2.2.12 (Ubuntu)'), ('last-modified', 'Tue, 23 Oct 2012 04:46:36 GMT'), ('etag', 'W/"567dd-105f-4ccb2a7a9a500"'), ('date', 'Tue, 23 Oct 2012 04:46:36 GMT'), ('content-type', 'application/pdf')]
multipart/form-data; boundary=----------lImIt_of_THE_fIle_eW_$
And I didn't try requests,Coz I would like to solve this with httplib(without any external lib)
Solution
To post parameters and a file in a body you could use multipart/form-data
content type:
#!/usr/bin/env python
import requests # $ pip install requests
file = 'file content as a file object or string'
r = requests.post('http://example.com/SomeName/action.php',
files={'file': ('filename.txt', file)},
data={'str1': 'string1', 'str2': 'string2'})
print(r.text) # response
requests.post
sends to the server something like-this:
POST /SomeName/action.php HTTP/1.1
Host: example.com
Content-Length: 449
Content-Type: multipart/form-data; boundary=f27f8ef67cac403aaaf433f83742bd64
Accept-Encoding: identity, deflate, compress, gzip
Accept: */*
--f27f8ef67cac403aaaf433f83742bd64
Content-Disposition: form-data; name="str2"
Content-Type: text/plain
string2
--f27f8ef67cac403aaaf433f83742bd64
Content-Disposition: form-data; name="str1"
Content-Type: text/plain
string1
--f27f8ef67cac403aaaf433f83742bd64
Content-Disposition: form-data; name="file"; filename="filename.txt"
Content-Type: text/plain
file content as a file object or string
--f27f8ef67cac403aaaf433f83742bd64--
To reproduce it with httplib
see POST form-data with Python example.
A simpler solution if your parameters do not contain much data is to pass them in the url query part and leave the body to contain only the file:
#!/usr/bin/env python
import urllib
import requests # $ pip install requests
params = {'str1': 'string1', 'str2': 'string2', 'filename': 'filename.txt'}
file = 'file content as a file object or string, etc'
url = 'http://example.com/SomeName/action.php?' + urllib.urlencode(params)
r = requests.post(url, data=file, headers={'Content-Type': 'text/plain'})
print(r.text) # response
It corresponds to the following HTTP request:
POST /SomeName/action.php?str2=string2&str1=string1&filename=filename.txt HTTP/1.1
Host: example.com
Content-Length: 39
Content-Type: text/plain
Accept-Encoding: identity, deflate, compress, gzip
Accept: */*
file content as a file object or string
It should be easier to translate to httplib
if you need it.
OTHER TIPS
The following code can also solve the problem with transfering file with other meta data using httplib (with out any external libraries):
def document_management_service_success(self,str_loc_file_path,*args):
locfile = open(str_loc_file_path,'rb').read()
str_loc_file = locfile.split('#end_pymotw_header')
initial_data = str_loc_file[0]
encoded_data = ''.join("{0:08b}".format(ord(c)) for c in initial_data)
params = urllib.urlencode({'docName':'INVOICE', 'docNumber':'RF/2', 'cusName':'Tijo john', 'cusNumber':'C124', 'category':'INVOICE', 'data':encoded_data})
headers = {"Accept": "Text/*","Content-Disposition":"form-data" ,"Content-Type": "application/x-www-form-urlencoded, application/pdf, form-data"}
conn = httplib.HTTPConnection("efiling.nucoreindia.com")
conn.connect()
conn.set_debuglevel(1)
conn.request("POST", "/api/?task=create", params, headers)
response = conn.getresponse()
print "Server Response status is"+str(response.status)+"and Reason is,"+str(response.reason)
print response.getheaders()
print response.read()
pass