2 from cStringIO import StringIO
8 Base class for upload objects. This class should always be subclassed
9 by upload classes and its constructor always be called.
11 Upload classes are file like object/iterators that have additional
12 variables length and content_type.
17 def __init__(self, length, content_type):
19 self.content_type = content_type
25 data = self.read(self.BLOCK_SIZE)
34 elif type(s) is unicode:
35 return s.encode('utf-8')
40 class UploadRawData(Upload):
43 This upload class is simply a wrapper around StringIO
46 def __init__(self, data, content_type='application/x-www-form-urlencoded'):
47 self.fstr = StringIO(data)
48 Upload.__init__(self, len(data), content_type)
50 def read(self, length=-1):
51 return self.fstr.read(length)
54 class UploadDict(UploadRawData):
57 This class creates an x-www-form-urlencoded representation of a dict
58 and then passes it through its parent UploadRawData
61 def __init__(self, data):
62 postdata = '&'.join('%s=%s' % (self.encode(i), self.encode(data[i])) for i in data)
63 UploadRawData.__init__(self, postdata)
66 class UploadFile(Upload):
69 This class accepts a file with information and a postdata dictionary
70 and creates a multipart/form-data representation from it.
78 def __init__(self, filefield, filename, filelength, file, data):
79 self.stage = self.STAGE_FILEHEADER
80 self.boundary = self.generate_boundary()
81 self.postdata = self.generate_multipart_from_dict(data)
82 self.footer = '\r\n--%s--\r\n' % self.boundary
83 self.fileheader = ('--%s\r\n' % self.boundary +
84 'Content-Disposition: form-data; name="%s"; filename="%s"\r\n' %
85 (self.encode(filefield), self.encode(filename)) +
86 'Content-Type: application/octet-stream\r\n\r\n')
88 self.length_left = filelength
91 Upload.__init__(self, len(self.fileheader) + filelength + len(self.postdata) + len(self.footer) + 2,
92 'multipart/form-data; boundary=' + self.boundary)
94 def read(self, length):
95 if self.stage == self.STAGE_DONE:
97 elif self.stage != self.STAGE_FILE:
98 if self.str_data is None:
99 if self.stage == self.STAGE_FILEHEADER:
100 self.str_data = StringIO(self.fileheader)
101 elif self.stage == self.STAGE_POSTDATA:
102 self.str_data = StringIO(self.postdata)
103 elif self.stage == self.STAGE_FOOTER:
104 self.str_data = StringIO(self.footer)
105 data = self.str_data.read(length)
108 if length > self.length_left:
109 length = self.length_left
110 data = self.file.read(length)
111 self.length_left -= len(data)
119 return self.read(length)
123 def generate_boundary():
124 return '----%s----' % ''.join((random.choice(
125 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')
126 for i in xrange(32)))
128 def generate_multipart_from_dict(self, data):
131 postdata.append('--' + self.boundary)
132 postdata.append('Content-Disposition: form-data; name="%s"' % self.encode(i))
134 postdata.append(self.encode(data[i]))
135 return '\r\n'.join(postdata)