Newer
Older
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import random
from cStringIO import StringIO
class Upload(object):
"""
Base class for upload objects. This class should always be subclassed
by upload classes and its constructor always be called.
Upload classes are file like object/iterators that have additional
variables length and content_type.
"""
BLOCK_SIZE = 8192
def __init__(self, length, content_type):
self.length = length
self.content_type = content_type
def __iter__(self):
return self
def next(self):
data = self.read(self.BLOCK_SIZE)
if data == '':
raise StopIteration
return data
@staticmethod
def encode(s):
if type(s) is str:
return s
elif type(s) is unicode:
return s.encode('utf-8')
else:
return s
class UploadRawData(Upload):
"""
This upload class is simply a wrapper around StringIO
"""
def __init__(self, data, content_type = 'application/x-www-form-urlencoded'):
self.fstr = StringIO(data)
Upload.__init__(self, len(data), content_type)
def read(self, length = -1):
return self.fstr.read(length)
class UploadDict(UploadRawData):
"""
This class creates an x-www-form-urlencoded representation of a dict
and then passes it through its parent UploadRawData
"""
def __init__(self, data):
postdata = '&'.join('%s=%s' % (self.encode(i), self.encode(data[i])) for i in data)
UploadRawData.__init__(self, postdata)
class UploadFile(Upload):
"""
This class accepts a file with information and a postdata dictionary
and creates a multipart/form-data representation from it.
"""
STAGE_FILEHEADER = 0
STAGE_FILE = 1
STAGE_POSTDATA = 2
STAGE_FOOTER = 3
STAGE_DONE = 4
def __init__(self, filefield, filename, filelength, file, data):
self.stage = self.STAGE_FILEHEADER;
self.boundary = self.generate_boundary()
self.postdata = self.generate_multipart_from_dict(data)
self.footer = '\r\n--%s--\r\n' % self.boundary
self.fileheader = ('--%s\r\n' % self.boundary +
'Content-Disposition: form-data; name="%s"; filename="%s"\r\n' %
(self.encode(filefield), self.encode(filename)) +
'Content-Type: application/octet-stream\r\n\r\n')
self.file = file
self.length_left = filelength
self.str_data = None
Upload.__init__(self, len(self.fileheader) + filelength + len(self.postdata) + len(self.footer) + 2,
'multipart/form-data; boundary=' + self.boundary)
def read(self, length):
if self.stage == self.STAGE_DONE:
return ''
elif self.stage != self.STAGE_FILE:
if self.str_data is None:
if self.stage == self.STAGE_FILEHEADER:
self.str_data = StringIO(self.fileheader)
self.str_data = StringIO(self.postdata)
elif self.stage == self.STAGE_FOOTER:
self.str_data = StringIO(self.footer)
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
data = self.str_data.read(length)
else:
if self.length_left:
if length > self.length_left:
length = self.length_left
data = self.file.read(length)
self.length_left -= len(data)
else:
self.stage += 1
return '\r\n'
if data == '':
self.stage += 1
self.str_data = None
return self.read(length)
return data
@staticmethod
def generate_boundary():
return '----%s----' % ''.join((random.choice(
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')
for i in xrange(32)))
def generate_multipart_from_dict(self, data):
postdata = []
for i in data:
postdata.append('--' + self.boundary)
postdata.append('Content-Disposition: form-data; name="%s"' % self.encode(i))
postdata.append('')
postdata.append(self.encode(data[i]))
return '\r\n'.join(postdata)