comparison tests/multipart.py @ 63:af1476a936fc

add a multipart processor so we can post damn files
author Jeff Hammel <jhammel@mozilla.com>
date Thu, 01 Mar 2012 16:57:00 -0800
parents
children bb8d993376aa
comparison
equal deleted inserted replaced
62:7c154953acc4 63:af1476a936fc
1 """
2 from
3 http://www.doughellmann.com/PyMOTW/urllib2/#uploading-files
4
5 Very very sad that python2 stdlib doesn't have this
6 """
7
8 import itertools
9 import mimetools
10 import mimetypes
11 from cStringIO import StringIO
12 import urllib
13 import urllib2
14
15 class MultiPartForm(object):
16 """Accumulate the data to be used when posting a form."""
17
18 def __init__(self):
19 self.form_fields = []
20 self.files = []
21 self.boundary = mimetools.choose_boundary()
22
23 def get_content_type(self):
24 return 'multipart/form-data; boundary=%s' % self.boundary
25
26 def add_field(self, name, value):
27 """Add a simple field to the form data."""
28 self.form_fields.append((name, value))
29
30 def add_file(self, fieldname, filename, fileHandle, mimetype=None):
31 """Add a file to be uploaded."""
32 body = fileHandle.read()
33 if mimetype is None:
34 mimetype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
35 self.files.append((fieldname, filename, mimetype, body))
36
37 def post(self, url):
38 request = urllib2.Request(url)
39 body = str(self)
40 request.add_header('Content-type', self.get_content_type())
41 request.add_header('Content-length', len(body))
42 request.add_data(body)
43 return urllib2.urlopen(request)
44
45 def __str__(self):
46 """Return a string representing the form data, including attached files."""
47 # Build a list of lists, each containing "lines" of the
48 # request. Each part is separated by a boundary string.
49 # Once the list is built, return a string where each
50 # line is separated by '\r\n'.
51 parts = []
52 part_boundary = '--' + self.boundary
53
54 # Add the form fields
55 parts.extend(
56 [ part_boundary,
57 'Content-Disposition: form-data; name="%s"' % name,
58 '',
59 value,
60 ]
61 for name, value in self.form_fields
62 )
63
64 # Add the files to upload
65 parts.extend(
66 [ part_boundary,
67 'Content-Disposition: file; name="%s"; filename="%s"' % \
68 (field_name, filename),
69 'Content-Type: %s' % content_type,
70 '',
71 body,
72 ]
73 for field_name, filename, content_type, body in self.files
74 )
75
76 # Flatten the list and add closing boundary marker,
77 # then return CR+LF separated data
78 flattened = list(itertools.chain(*parts))
79 flattened.append('--' + self.boundary + '--')
80 flattened.append('')
81 return '\r\n'.join(flattened)