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
87
88
89
90
91
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
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)
elif self.stage == self.STAGE_POSTDATA:
self.str_data = StringIO(self.postdata)
elif self.stage == self.STAGE_FOOTER:
self.str_data = StringIO(self.footer)
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)