A scripted web client that will post data to a site as if from a form using ENCTYPE="multipart/form-data". This is typically used to upload files, but also gets around a server's (e.g. ASP's) limitation on the amount of data that can be accepted via a standard POST (application/x-www-form-urlencoded).
| Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | import httplib, mimetypes
def post_multipart(host, selector, fields, files):
"""
Post fields and files to an http host as multipart/form-data.
fields is a sequence of (name, value) elements for regular form fields.
files is a sequence of (name, filename, value) elements for data to be uploaded as files
Return the server's response page.
"""
content_type, body = encode_multipart_formdata(fields, files)
h = httplib.HTTP(host)
h.putrequest('POST', selector)
h.putheader('content-type', content_type)
h.putheader('content-length', str(len(body)))
h.endheaders()
h.send(body)
errcode, errmsg, headers = h.getreply()
return h.file.read()
def encode_multipart_formdata(fields, files):
"""
fields is a sequence of (name, value) elements for regular form fields.
files is a sequence of (name, filename, value) elements for data to be uploaded as files
Return (content_type, body) ready for httplib.HTTP instance
"""
BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$'
CRLF = '\r\n'
L = []
for (key, value) in fields:
L.append('--' + BOUNDARY)
L.append('Content-Disposition: form-data; name="%s"' % key)
L.append('')
L.append(value)
for (key, filename, value) in files:
L.append('--' + BOUNDARY)
L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
L.append('Content-Type: %s' % get_content_type(filename))
L.append('')
L.append(value)
L.append('--' + BOUNDARY + '--')
L.append('')
body = CRLF.join(L)
content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
return content_type, body
def get_content_type(filename):
return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
|
Discussion
At Python 9, Moshe Zadka showed how to create the multipart-mime data using MimeWriter ( http://www.python9.org/p9-zadka.ppt ). His recipe worked just fine for me when I was talking to a Zope server, but triggered a cryptic error message when pointed at an ASP server that was using the COM file upload component from http://persits.com .
The main problem with MimeWriter is that it does not use '\r\n' for newlines, and the ASP server insisted on this. Other bits of persnicketiness on the part of the ASP included rejection of a mime message that had content-type headers in its regular form fields, and insistence on at least five dashes at the front of the boundary marker.
The function encode_multipart_formdata() shown here takes a more direct approach to creating the mime data, and fairly closely mimics the data sent by Internet Explorer 5.5.


Comments
python mod. I made a wrapper to urllib2.urlopen() in order to support file uploading
http://fabien.seisen.org/python/
It uses boundary creation from mimetools and doesn't read the whole file in memory
using urls.
This allows you to specify the form as a url and not worry about host and selector.
Update to use HTTPConnection. simple update to use HTTPConnection instead of HTTP for the recipe to simplify it and also to use HTTP 1.1.
Only replace first function of the recipe:
Should work as the original version.
extended return... new version does additionally return Status and Reason information. For exact same return of original version replace
with
With cookie support on Python 2.4. Here's a version of your code that supports cookies with python 2.4's urllib2 and cookielib.
a less intrusive version using the urllib2 hierarchy. Here is the same basic idea, but using a class inherited into the BasicHandler hierarchy of urllib2. It has the advantage of leaving all the existing urllib2 functionality intact.
Example usage:
The code is at: http://odin.himinbi.org/MultipartPostHandler.py
MultipartPostHandler didn't work for unicode files. I fixed it by reading in via StringIO class.
fix posted here:
http://peerit.blogspot.com/2007/07/multipartposthandler-doesnt-work-for.html
Script which can be called from the command line. This script is based on this recipe and can be called from the shell:
http://fabien.seisen.org/python/urllib2_multipart.html
Do NOT use this script verbatim. It relies on a deprecated backward-compatibility module in httplib that didn't work at all for me. When I switched to using the more modern HTTPConnection version (described by another commenter above) it worked correctly first time out of the box, so I strongly encourage you to use that instead.
how to deal this situation? I have a question in my case. I cannot find the solution, can anybody me out? thank.
Even for logout action, I have to supply the 'data' var as following
If I do not supply 'data', I was told "you are not logged in"
So, in this case, how can I supply all of utmpnum, utmpkey, utmpuserid and the attached file?
I have this code, but the response still says "you are not logged in"!
Take a look at recipe 576422: Python HTTP POST binary file upload with pycurl (http://code.activestate.com/recipes/576422/)
Sign in to comment