4
# - UTF-8 filenames are now allowed (Eli Golovinsky)<br/>
5
# - File object is no more mandatory, Object only needs to have seek() read() attributes (Eli Golovinsky)<br/>
8
# - upload is now done with chunks (Adam Ambrose)
12
# bug fix: kosh @T aesaeion.com
13
# HTTPS support : Ryan Grow <ryangrow @T yahoo.com>
15
# Copyright (C) 2004,2005,2006 Fabien SEISEN
17
# This library is free software; you can redistribute it and/or
18
# modify it under the terms of the GNU Lesser General Public
19
# License as published by the Free Software Foundation; either
20
# version 2.1 of the License, or (at your option) any later version.
22
# This library is distributed in the hope that it will be useful,
23
# but WITHOUT ANY WARRANTY; without even the implied warranty of
24
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25
# Lesser General Public License for more details.
27
# You should have received a copy of the GNU Lesser General Public
28
# License along with this library; if not, write to the Free Software
29
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31
# you can contact me at: <fabien@seisen.org>
32
# http://fabien.seisen.org/python/
34
# Also modified by Adam Ambrose (aambrose @T pacbell.net) to write data in
35
# chunks (hardcoded to CHUNK_SIZE for now), so the entire contents of the file
36
# don't need to be kept in memory.
39
enable to upload files using multipart/form-data
42
upload files in python:
43
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306
45
timeoutsocket.py: overriding Python socket API:
46
http://www.timo-tasi.org/python/timeoutsocket.py
47
http://mail.python.org/pipermail/python-announce-list/2001-December/001095.html
51
u = urllib2.urlopen('http://site.com/path' [, data])
53
data can be a mapping object or a sequence of two-elements tuples
54
(like in original urllib2.urlopen())
55
varname still need to be a string and
56
value can be string of a file object
81
def __init__(self, fd, content_type):
83
self.content_type = content_type
85
def get_content_type(filename):
86
return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
88
# if sock is None, juste return the estimate size
89
def send_data(v_vars, v_files, boundary, sock=None):
93
buffer += '--%s\r\n' % boundary
94
buffer += 'Content-Disposition: form-data; name="%s"\r\n' % k
100
for (k, v) in v_files:
102
file_size = os.fstat(fd.fileno())[stat.ST_SIZE]
103
name = fd.name.split('/')[-1]
104
if isinstance(name, unicode):
105
name = name.encode('UTF-8')
107
buffer += '--%s\r\n' % boundary
108
buffer += 'Content-Disposition: form-data; name="%s"; filename="%s"\r\n' \
111
if v.content_type != None:
112
content_type = v.content_type
114
content_type = get_content_type(name)
115
buffer += 'Content-Type: %s\r\n' % content_type
116
buffer += 'Content-Length: %ld\r\n' % file_size
122
if hasattr(fd, 'seek'):
125
chunk = fd.read(CHUNK_SIZE)
131
buffer += '--%s--\r\n' % boundary
138
# mainly a copy of HTTPHandler from urllib2
139
class newHTTPHandler(urllib2.BaseHandler):
140
def http_open(self, req):
141
return self.do_open(httplib.HTTP, req)
143
def do_open(self, http_class, req):
144
data = req.get_data()
147
# mapping object (dict)
148
if req.has_data() and type(data) != str:
149
if hasattr(data, 'items'):
153
if len(data) and not isinstance(data[0], tuple):
156
ty, va, tb = sys.exc_info()
157
raise TypeError, "not a valid non-string sequence or mapping object", tb
160
if isinstance(v, FileUpload):
161
v_files.append((k, v))
163
v_vars.append( (k, v) )
164
# no file ? convert to string
165
if len(v_vars) > 0 and len(v_files) == 0:
166
data = urllib.urlencode(v_vars)
169
host = req.get_host()
171
raise urllib2.URLError('no host given')
173
h = http_class(host) # will parse host:port
175
h.putrequest('POST', req.get_selector())
176
if not 'Content-type' in req.headers:
178
boundary = mimetools.choose_boundary()
179
l = send_data(v_vars, v_files, boundary)
180
h.putheader('Content-Type',
181
'multipart/form-data; boundary=%s' % boundary)
182
h.putheader('Content-length', str(l))
184
h.putheader('Content-type',
185
'application/x-www-form-urlencoded')
186
if not 'Content-length' in req.headers:
187
h.putheader('Content-length', '%d' % len(data))
189
h.putrequest('GET', req.get_selector())
191
scheme, sel = urllib.splittype(req.get_selector())
192
sel_host, sel_path = urllib.splithost(sel)
193
h.putheader('Host', sel_host or host)
194
for name, value in self.parent.addheaders:
195
name = name.capitalize()
196
if name not in req.headers:
197
h.putheader(name, value)
198
for k, v in req.headers.items():
200
# httplib will attempt to connect() here. be prepared
201
# to convert a socket error to a URLError.
204
except socket.error, err:
205
raise urllib2.URLError(err)
209
l = send_data(v_vars, v_files, boundary, h)
210
elif len(v_vars) > 0:
211
# if data is passed as dict ...
212
data = urllib.urlencode(v_vars)
215
# "normal" urllib2.urlopen()
218
code, msg, hdrs = h.getreply()
221
resp = urllib.addinfourl(fp, hdrs, req.get_full_url())
226
return self.parent.error('http', req, fp, code, msg, hdrs)
228
#urllib2._old_HTTPHandler = urllib2.HTTPHandler
229
#urllib2.HTTPHandler = newHTTPHandler
231
class newHTTPSHandler(newHTTPHandler):
232
def https_open(self, req):
233
return self.do_open(httplib.HTTPS, req)
235
#urllib2.HTTPSHandler = newHTTPSHandler
237
#if __name__ == '__main__':
240
# import urllib2_file
244
# def usage(progname):
246
#SYNTAX: %s -u url -f file [-v]
250
# opts, args = getopt.getopt(sys.argv[1:], 'hvu:f:')
251
# except getopt.GetoptError, errmsg:
252
# print "ERROR:", errmsg
259
# for name, value in opts:
260
# if name in ('-h',):
263
# elif name in ('-v',):
265
# elif name in ('-u',):
267
# elif name in ('-f',):
270
# print "invalid argument:", name
284
# fd = open(v_file, 'r')
288
# # u = urllib2.urlopen(v_url, data)
289
# req = urllib2.Request(v_url, data, {})
291
# u = urllib2.urlopen(req)
292
# except urllib2.HTTPError, errobj:
293
# print "HTTPError:", errobj.code