Ticket #2070: 2070_revision7359.diff
File 2070_revision7359.diff, 76.9 KB (added by , 17 years ago) |
---|
-
django/http/multipartparser.py
1 """ 2 MultiPart parsing for file uploads. 3 4 This object will take the file upload headers 5 and the file upload handler and chunk the upload 6 data for the handler to deal with. 7 """ 8 from django.utils.datastructures import MultiValueDict 9 from django.utils.encoding import force_unicode 10 11 __all__ = ('MultiPartParser','MultiPartParserError','InputStreamExhausted') 12 13 class MultiPartParserError(Exception): 14 pass 15 16 class InputStreamExhausted(Exception): 17 """ No more reads are allowed from this device. """ 18 pass 19 20 class MultiPartParser(object): 21 """ 22 A rfc2388 multipart/form-data parser. 23 24 parse() reads the input stream in chunk_size chunks and returns a 25 tuple of (POST MultiValueDict, FILES MultiValueDict). If 26 file_upload_dir is defined files will be streamed to temporary 27 files in the specified directory. 28 """ 29 def __init__(self, META, input_data, upload_handlers, encoding=None): 30 """ 31 Initialize the MultiPartParser object. 32 33 *META* -- The standard META dictionary in Django request objects. 34 *input_data* -- The raw post data, as a bytestring. 35 *upload_handler* -- An object of type UploadHandler 36 that performs operations on the uploaded 37 data. 38 *encoding* -- The encoding with which to treat the incoming data. 39 """ 40 # Import cgi utilities for (near) future use. 41 global parse_header, valid_boundary, settings 42 from django.conf import settings 43 from cgi import valid_boundary, parse_header 44 45 # 46 # Content-Type should containt multipart and the boundary information. 47 # 48 49 content_type = META.get('HTTP_CONTENT_TYPE', META.get('CONTENT_TYPE', '')) 50 if not content_type.startswith('multipart/'): 51 raise MultiPartParserError('Invalid Content-Type: %s' % 52 content_type) 53 54 # Parse the header to get the boundary to split the parts. 55 ctypes, opts = parse_header(content_type) 56 boundary = opts.get('boundary') 57 if not boundary or not valid_boundary(boundary): 58 raise MultiPartParserError('Invalid boundary in multipart: %s' % 59 boundary) 60 61 62 # 63 # Content-Length should contain the length of the body we are about 64 # to receive. 65 # 66 try: 67 content_length = int(META.get('HTTP_CONTENT_LENGTH', 68 META.get('CONTENT_LENGTH',0))) 69 except (ValueError, TypeError): 70 # For now set it to 0...we'll try again later on down. 71 content_length = 0 72 73 if content_length <= 0: 74 # This means we shouldn't continue...raise an error. 75 raise MultiPartParserError("Invalid content length: %r" % content_length) 76 77 self._boundary = boundary 78 self._input_data = input_data 79 80 # For compatibility with low-level network APIs (with 32-bit integers), 81 # the chunk size should be < 2^31, but still divisible by 4. 82 self._chunk_size = min(2147483644, *[x.chunk_size for x in upload_handlers 83 if x.chunk_size]) 84 85 self._meta = META 86 self._encoding = encoding or settings.DEFAULT_CHARSET 87 self._content_length = content_length 88 self._upload_handlers = upload_handlers 89 90 def parse(self): 91 """ 92 Parse the POST data and break it into a FILES MultiValueDict 93 and a POST MultiValueDict. 94 95 *returns* -- A tuple containing the POST and FILES dictionary, 96 respectively. 97 """ 98 from django.core.files.fileuploadhandler import StopUpload, SkipFile 99 from django.http import QueryDict 100 101 encoding = self._encoding 102 handlers = self._upload_handlers 103 104 limited_input_data = LimitBytes(self._input_data, self._content_length) 105 106 # See if the handler will want to take care of the parsing. 107 # This allows overriding everything if somebody wants it. 108 for handler in handlers: 109 result = handler.handle_raw_input(limited_input_data, 110 self._meta, 111 self._content_length, 112 self._boundary, 113 encoding) 114 if result is not None: 115 return result[0], result[1] 116 117 # Create the data structures to be used later. 118 self._post = QueryDict('', mutable=True) 119 self._files = MultiValueDict() 120 121 # Instantiate the parser and stream: 122 stream = LazyStream(ChunkIter(limited_input_data, self._chunk_size)) 123 124 # Whether or not to signal a file-completion at the beginning of the loop. 125 old_field_name = None 126 counters = [0] * len(handlers) 127 128 for item_type, meta_data, stream in Parser(stream, self._boundary): 129 if old_field_name: 130 # We run this at the beginning of the next loop 131 # since we cannot be sure a file is complete until 132 # we hit the next boundary/part of the multipart content. 133 self.handle_file_complete(old_field_name, counters) 134 135 try: 136 disposition = meta_data['content-disposition'][1] 137 field_name = disposition['name'].strip() 138 except (KeyError, IndexError, AttributeError): 139 continue 140 141 transfer_encoding = meta_data.get('content-transfer-encoding') 142 field_name = force_unicode(field_name, encoding, errors='replace') 143 144 if item_type == 'FIELD': 145 # This is a post field, we can just set it in the post 146 if transfer_encoding == 'base64': 147 raw_data = stream.read() 148 try: 149 data = str(raw_data).decode('base64') 150 except: 151 data = raw_data 152 else: 153 data = stream.read() 154 155 self._post.appendlist(field_name, 156 force_unicode(data, encoding, errors='replace')) 157 elif item_type == 'FILE': 158 # This is a file, use the handler... 159 file_successful = True 160 file_name = self.IE_sanitize(disposition.get('filename')) 161 if not file_name: 162 continue 163 164 file_name = force_unicode(file_name, encoding, errors='replace') 165 166 content_type = meta_data.get('content-type', ('',))[0].strip() 167 try: 168 charset = meta_data.get('content-type', (0,{}))[1].get('charset', None) 169 except: 170 charset = None 171 172 try: 173 content_length = int(meta_data.get('content-length')[0]) 174 except (IndexError, TypeError, ValueError): 175 content_length = None 176 177 counters = [0] * len(handlers) 178 try: 179 for handler in handlers: 180 retval = handler.new_file(field_name, file_name, 181 content_type, content_length, 182 charset) 183 if retval: 184 break 185 186 for chunk in stream: 187 if transfer_encoding == 'base64': 188 # We only special-case base64 transfer encoding 189 try: 190 chunk = str(chunk).decode('base64') 191 except Exception, e: 192 # Since this is only a chunk, any error is an unfixable error. 193 raise MultiValueParseError("Could not decode base64 data: %r" % e) 194 195 for i, handler in enumerate(handlers): 196 chunk_length = len(chunk) 197 counters[i] += chunk_length 198 chunk = handler.receive_data_chunk(chunk, 199 counters[i] - chunk_length, 200 counters[i]) 201 if chunk is None: 202 # If the chunk received by the handler is None, then don't continue. 203 break 204 205 except (StopUpload, SkipFile), e: 206 file_successful = False 207 if isinstance(e, SkipFile): 208 # Just use up the rest of this file... 209 stream.exhaust() 210 elif isinstance(e, StopUpload): 211 # Abort the parsing and break 212 parser.abort() 213 break 214 else: 215 # Handle file upload completions on next iteration. 216 old_field_name = field_name 217 else: 218 # If this is neither a FIELD or a FILE, just exhaust the stream. 219 stream.exhuast() 220 221 # Make sure that the request data is all fed 222 limited_input_data.exhaust() 223 224 # Signal that the upload has completed. 225 for handler in handlers: 226 retval = handler.upload_complete() 227 if retval: 228 break 229 230 return self._post, self._files 231 232 def handle_file_complete(self, old_field_name, counters): 233 """ 234 Handle all the signalling that takes place when a file is complete. 235 """ 236 for i, handler in enumerate(self._upload_handlers): 237 file_obj = handler.file_complete(counters[i]) 238 if file_obj: 239 # If it returns a file object, then set the files dict. 240 self._files.appendlist(force_unicode(old_field_name, 241 self._encoding, 242 errors='replace'), 243 file_obj) 244 break 245 246 def IE_sanitize(self, filename): 247 """cleanup filename from IE full paths""" 248 return filename and filename[filename.rfind("\\")+1:].strip() 249 250 251 252 class LazyStream(object): 253 """ 254 The LazyStream wrapper allows one to pull and "unget" bytes from a stream. 255 256 Given a producer object (an iterator that yields bytestrings), the 257 LazyStream object will support iteration, reading, and keeping a 258 "look-back" variable in case you need to "unget" some bytes. 259 """ 260 def __init__(self, producer, length=None): 261 """ 262 Every LazyStream must have a producer when instantiated. 263 264 A producer is an iterable that returns a string each time it 265 is called. 266 """ 267 self._producer = producer 268 self._empty = False 269 self._leftover = '' 270 self.length = length 271 self.position = 0 272 self._remaining = length 273 274 def tell(self): 275 return self.position 276 277 def read(self, size=None): 278 def parts(): 279 remaining = (size is not None and [size] or [self._remaining])[0] 280 # do the whole thing in one shot if no limit was provided. 281 if remaining is None: 282 yield ''.join(self) 283 return 284 285 # otherwise do some bookkeeping to return exactly enough 286 # of the stream and stashing any extra content we get from 287 # the producer 288 while remaining != 0: 289 assert remaining > 0, 'remaining bytes to read should never go negative' 290 291 chunk = self.next() 292 293 emitting = chunk[:remaining] 294 self.unget(chunk[remaining:]) 295 remaining -= len(emitting) 296 yield emitting 297 298 out = ''.join(parts()) 299 self.position += len(out) 300 return out 301 302 def next(self): 303 """ 304 Used when the exact number of bytes to read is unimportant. 305 306 This procedure just returns whatever is chunk is conveniently 307 returned from the iterator instead. Useful to avoid 308 unnecessary bookkeeping if performance is an issue. 309 """ 310 if self._leftover: 311 output = self._leftover 312 self.position += len(output) 313 self._leftover = '' 314 return output 315 else: 316 output = self._producer.next() 317 self.position += len(output) 318 return output 319 320 def close(self): 321 """ 322 Used to invalidate/disable this lazy stream. 323 324 Replaces the producer with an empty list. Any leftover bytes 325 that have already been read will still be reported upon read() 326 and/or next(). 327 """ 328 self._producer = [] 329 330 def __iter__(self): 331 return self 332 333 def unget(self, bytes): 334 """ 335 Places bytes back onto the front of the lazy stream. 336 337 Future calls to read() will return those bytes first. The 338 stream position and thus tell() will be rewound. 339 """ 340 self.position -= len(bytes) 341 self._leftover = ''.join([bytes, self._leftover]) 342 343 def exhaust(self): 344 """ 345 Exhausts the entire underlying stream. 346 347 Useful for skipping and advancing sections. 348 """ 349 for thing in self: 350 pass 351 352 353 class ChunkIter(object): 354 """ 355 An iterable that will yield chunks of data. 356 Given a file-like object as the constructor, 357 this object will yield chunks of read operations 358 from that object. 359 """ 360 def __init__(self, flo, chunk_size=64 * 1024): 361 self.flo = flo 362 self.chunk_size = chunk_size 363 364 def next(self): 365 try: 366 data = self.flo.read(self.chunk_size) 367 except InputStreamExhausted: 368 raise StopIteration 369 if data: 370 return data 371 else: 372 raise StopIteration 373 374 def __iter__(self): 375 return self 376 377 378 class LimitBytes(object): 379 """ Limit bytes for a file object. """ 380 def __init__(self, fileobject, length): 381 self._file = fileobject 382 self.remaining = length 383 384 def read(self, num_bytes=None): 385 """ 386 Read data from the underlying file. 387 If you ask for too much or there isn't anything left, 388 this will raise an InputStreamExhausted error. 389 """ 390 if self.remaining <= 0: 391 raise InputStreamExhausted() 392 if num_bytes is None: 393 num_bytes = self.remaining 394 else: 395 num_bytes = min(num_bytes, self.remaining) 396 self.remaining -= num_bytes 397 return self._file.read(num_bytes) 398 399 def exhaust(self): 400 """ 401 Exhaust this file until all of the bytes it was limited by 402 have been read. 403 """ 404 while self.remaining > 0: 405 num_bytes = min(self.remaining, 16384) 406 __ = self._file.read(num_bytes) 407 self.remaining -= num_bytes 408 409 410 class InterBoundaryIter(object): 411 """ 412 A Producer that will iterate over boundaries. 413 """ 414 def __init__(self, stream, boundary): 415 self._stream = stream 416 self._boundary = boundary 417 418 def __iter__(self): 419 return self 420 421 def next(self): 422 try: 423 return LazyStream(BoundaryIter(self._stream, self._boundary)) 424 except InputStreamExhausted: 425 raise StopIteration 426 427 428 class BoundaryIter(object): 429 """ 430 A Producer that is sensitive to boundaries. 431 432 Will happily yield bytes until a boundary is found. Will yield the 433 bytes before the boundary, throw away the boundary bytes 434 themselves, and push the post-boundary bytes back on the stream. 435 436 The future calls to .next() after locating the boundary will raise 437 a StopIteration exception. 438 """ 439 440 def __init__(self, stream, boundary): 441 self._stream = stream 442 self._boundary = boundary 443 self._done = False 444 # rollback an additional six bytes because the format is like 445 # this: CRLF<boundary>[--CRLF] 446 self._rollback = len(boundary) + 6 447 448 # Try to use mx fast string search if available. Otherwise 449 # use Python find. Wrap the latter for consistency. 450 unused_char = self._stream.read(1) 451 if not unused_char: 452 raise InputStreamExhausted 453 self._stream.unget(unused_char) 454 try: 455 from mx.TextTools import FS 456 self._fs = FS(boundary).find 457 except ImportError: 458 self._fs = lambda data: data.find(boundary) 459 460 def __iter__(self): 461 return self 462 463 def next(self): 464 if self._done: 465 raise StopIteration 466 467 stream = self._stream 468 rollback = self._rollback 469 470 bytes_read = 0 471 chunks = [] 472 for bytes in stream: 473 bytes_read += len(bytes) 474 chunks.append(bytes) 475 if bytes_read > rollback: 476 break 477 if not bytes: 478 break 479 else: 480 self._done = True 481 482 if not chunks: 483 raise StopIteration 484 485 chunk = ''.join(chunks) 486 boundary = self._find_boundary(chunk, len(chunk) < self._rollback) 487 488 if boundary: 489 end, next = boundary 490 stream.unget(chunk[next:]) 491 self._done = True 492 return chunk[:end] 493 else: 494 # make sure we dont treat a partial boundary (and 495 # its separators) as data 496 if not chunk[:-rollback]:# and len(chunk) >= (len(self._boundary) + 6): 497 # There's nothing left, we should just return and mark as done. 498 self._done = True 499 return chunk 500 else: 501 stream.unget(chunk[-rollback:]) 502 return chunk[:-rollback] 503 504 def _find_boundary(self, data, eof = False): 505 """ 506 Finds a multipart boundary in data. 507 508 Should no boundry exist in the data None is returned 509 instead. Otherwise a tuple containing 510 the indices of the following are returned: 511 512 * the end of current encapsulation 513 514 * the start of the next encapsulation 515 """ 516 index = self._fs(data) 517 if index < 0: 518 return None 519 else: 520 end = index 521 next = index + len(self._boundary) 522 data_len = len(data) - 1 523 # backup over CRLF 524 if data[max(0,end-1)] == '\n': end -= 1 525 if data[max(0,end-1)] == '\r': end -= 1 526 # skip over --CRLF 527 if data[min(data_len,next)] == '-': next += 1 528 if data[min(data_len,next)] == '-': next += 1 529 if data[min(data_len,next)] == '\r': next += 1 530 if data[min(data_len,next)] == '\n': next += 1 531 return end, next 532 533 534 def ParseBoundaryStream(stream, max_header_size): 535 """ 536 Parses one and exactly one stream that encapsulates a boundary. 537 """ 538 # Stream at beginning of header, look for end of header 539 # and parse it if found. The header must fit within one 540 # chunk. 541 chunk = stream.read(max_header_size) 542 # 'find' returns the top of these four bytes, so we'll 543 # need to munch them later to prevent them from polluting 544 # the payload. 545 header_end = chunk.find('\r\n\r\n') 546 547 def parse_header(line): 548 from cgi import parse_header 549 main_value_pair, params = parse_header(line) 550 try: 551 name, value = main_value_pair.split(':', 1) 552 except: 553 raise ValueError("Invalid header: %r" % line) 554 return name, (value, params) 555 556 if header_end == -1: 557 # we find no header, so we just mark this fact and pass on 558 # the stream verbatim 559 stream.unget(chunk) 560 return ('RAW', {}, stream) 561 562 header = chunk[:header_end] 563 564 # here we place any excess chunk back onto the stream, as 565 # well as throwing away the CRLFCRLF bytes from above. 566 stream.unget(chunk[header_end + 4:]) 567 568 is_file_field = False 569 outdict = {} 570 571 # Eliminate blank lines 572 for line in header.split('\r\n'): 573 # This terminology ("main value" and "dictionary of 574 # parameters") is from the Python docs. 575 name, (value, params) = parse_header(line) 576 if name == 'content-disposition' and params.get('filename'): 577 is_file_field = True 578 579 outdict[name] = value, params 580 581 if is_file_field: 582 return ('FILE', outdict, stream) 583 else: 584 return ('FIELD', outdict, stream) 585 586 587 class Parser(object): 588 def __init__(self, stream, boundary): 589 self._stream = stream 590 self._separator = '--' + boundary 591 592 def __iter__(self): 593 594 boundarystream = InterBoundaryIter(self._stream, 595 self._separator) 596 597 for sub_stream in boundarystream: 598 # Iterate over each part 599 yield ParseBoundaryStream(sub_stream, 1024) -
django/http/__init__.py
11 11 12 12 from django.utils.datastructures import MultiValueDict, FileDict 13 13 from django.utils.encoding import smart_str, iri_to_uri, force_unicode 14 14 from django.http.multipartparser import MultiPartParser 15 15 from utils import * 16 16 17 17 RESERVED_CHARS="!*'();:@&=+$,/?%#[]" … … 30 30 self.GET, self.POST, self.COOKIES, self.META, self.FILES = {}, {}, {}, {}, {} 31 31 self.path = '' 32 32 self.method = None 33 self._upload_handlers = [] 33 34 34 35 def __repr__(self): 35 36 return '<HttpRequest\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' % \ … … 102 103 103 104 encoding = property(_get_encoding, _set_encoding) 104 105 105 def parse_file_upload(header_dict, post_data): 106 """Returns a tuple of (POST QueryDict, FILES MultiValueDict).""" 107 import email, email.Message 108 from cgi import parse_header 109 raw_message = '\r\n'.join(['%s:%s' % pair for pair in header_dict.items()]) 110 raw_message += '\r\n\r\n' + post_data 111 msg = email.message_from_string(raw_message) 112 POST = QueryDict('', mutable=True) 113 FILES = MultiValueDict() 114 for submessage in msg.get_payload(): 115 if submessage and isinstance(submessage, email.Message.Message): 116 name_dict = parse_header(submessage['Content-Disposition'])[1] 117 # name_dict is something like {'name': 'file', 'filename': 'test.txt'} for file uploads 118 # or {'name': 'blah'} for POST fields 119 # We assume all uploaded files have a 'filename' set. 120 if 'filename' in name_dict: 121 assert type([]) != type(submessage.get_payload()), "Nested MIME messages are not supported" 122 if not name_dict['filename'].strip(): 123 continue 124 # IE submits the full path, so trim everything but the basename. 125 # (We can't use os.path.basename because that uses the server's 126 # directory separator, which may not be the same as the 127 # client's one.) 128 filename = name_dict['filename'][name_dict['filename'].rfind("\\")+1:] 129 FILES.appendlist(name_dict['name'], FileDict({ 130 'filename': filename, 131 'content-type': 'Content-Type' in submessage and submessage['Content-Type'] or None, 132 'content': submessage.get_payload(), 133 })) 134 else: 135 POST.appendlist(name_dict['name'], submessage.get_payload()) 136 return POST, FILES 106 def _set_upload_handlers(self, upload_handlers): 107 """ 108 Set the upload handler to the new handler given in the parameter. 109 """ 110 if hasattr(self, '_files'): 111 raise AttributeError("You cannot set the upload handler after the upload has been processed.") 112 self._upload_handlers = upload_handlers 137 113 114 def _get_upload_handlers(self): 115 return self._upload_handlers 138 116 117 upload_handlers = property(_get_upload_handlers, _set_upload_handlers) 118 119 def parse_file_upload(self, META, post_data): 120 """Returns a tuple of (POST QueryDict, FILES MultiValueDict).""" 121 from django.core.files.fileuploadhandler import TemporaryFileUploadHandler, MemoryFileUploadHandler 122 if not self.upload_handlers: 123 # The order of the upload handlers specifies the order in which 124 # the handlers receive data. One handler may or may not 125 # prevent the execution of subsequent handlers. 126 self.upload_handlers = (MemoryFileUploadHandler(), 127 TemporaryFileUploadHandler()) 128 else: 129 # By using tuple(), we render the upload_handlers 130 # data structure immutable. 131 self.upload_handlers = tuple(self.upload_handlers) 132 133 parser = MultiPartParser(META, post_data, self.upload_handlers, 134 self.encoding) 135 return parser.parse() 136 137 139 138 class QueryDict(MultiValueDict): 140 139 """ 141 140 A specialized MultiValueDict that takes a query string when initialized. -
django/test/client.py
18 18 BOUNDARY = 'BoUnDaRyStRiNg' 19 19 MULTIPART_CONTENT = 'multipart/form-data; boundary=%s' % BOUNDARY 20 20 21 class FakePayload(object): 22 """ 23 A wrapper around StringIO that restricts what can be read, 24 since data from the network can't be seeked and cannot 25 be read outside of its content length (or else we hang). 26 """ 27 def __init__(self, content): 28 self.__content = StringIO(content) 29 self.__len = len(content) 30 31 def read(self, num_bytes=None): 32 if num_bytes is None: 33 num_bytes = self.__len or 1 34 assert self.__len >= num_bytes, "Cannot read more than the available bytes from the HTTP incoming data." 35 content = self.__content.read(num_bytes) 36 self.__len -= num_bytes 37 return content 38 39 21 40 class ClientHandler(BaseHandler): 22 41 """ 23 42 A HTTP Handler that can be used for testing purposes. … … 230 249 'CONTENT_TYPE': content_type, 231 250 'PATH_INFO': urllib.unquote(path), 232 251 'REQUEST_METHOD': 'POST', 233 'wsgi.input': StringIO(post_data),252 'wsgi.input': FakePayload(post_data), 234 253 } 235 254 r.update(extra) 236 255 -
django/conf/global_settings.py
224 224 # Example: "http://media.lawrence.com" 225 225 MEDIA_URL = '' 226 226 227 # Directory to upload streamed files temporarily. 228 # A value of `None` means that it will use the default temporary 229 # directory for the server's operating system. 230 FILE_UPLOAD_TEMP_DIR = None 231 227 232 # Default formatting for date objects. See all available format strings here: 228 233 # http://www.djangoproject.com/documentation/templates/#now 229 234 DATE_FORMAT = 'N j, Y' -
django/db/models/base.py
13 13 from django.utils.datastructures import SortedDict 14 14 from django.utils.functional import curry 15 15 from django.utils.encoding import smart_str, force_unicode, smart_unicode 16 from django.core.files.filemove import file_move_safe 16 17 from django.conf import settings 17 18 from itertools import izip 18 19 import types … … 384 385 def _get_FIELD_size(self, field): 385 386 return os.path.getsize(self._get_FIELD_filename(field)) 386 387 387 def _save_FIELD_file(self, field, filename, raw_ contents, save=True):388 def _save_FIELD_file(self, field, filename, raw_field, save=True): 388 389 directory = field.get_directory_name() 389 390 try: # Create the date-based directory if it doesn't exist. 390 391 os.makedirs(os.path.join(settings.MEDIA_ROOT, directory)) 391 392 except OSError: # Directory probably already exists. 392 393 pass 394 395 if filename is None: 396 filename = raw_field.file_name 397 393 398 filename = field.get_filename(filename) 394 399 395 400 # If the filename already exists, keep adding an underscore to the name of … … 406 411 setattr(self, field.attname, filename) 407 412 408 413 full_filename = self._get_FIELD_filename(field) 409 fp = open(full_filename, 'wb') 410 fp.write(raw_contents) 411 fp.close() 414 if hasattr(raw_field, 'temporary_file_path'): 415 raw_field.close() 416 file_move_safe(raw_field.temporary_file_path(), full_filename) 417 else: 418 from django.core.files import filelocks 419 fp = open(full_filename, 'wb') 420 # exclusive lock 421 filelocks.lock(fp, filelocks.LOCK_EX) 422 # Stream it into the file, from where it is. 423 for chunk in raw_field.chunk(65535): 424 fp.write(chunk) 425 fp.close() 412 426 413 427 # Save the width and/or height, if applicable. 414 428 if isinstance(field, ImageField) and (field.width_field or field.height_field): -
django/db/models/fields/__init__.py
785 785 setattr(cls, 'get_%s_filename' % self.name, curry(cls._get_FIELD_filename, field=self)) 786 786 setattr(cls, 'get_%s_url' % self.name, curry(cls._get_FIELD_url, field=self)) 787 787 setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self)) 788 setattr(cls, 'save_%s_file' % self.name, lambda instance, filename, raw_contents, save=True: instance._save_FIELD_file(self, filename, raw_contents, save)) 788 setattr(cls, 'save_%s_file' % self.name, lambda instance, filename, raw_field, save=True: instance._save_FIELD_file(self, filename, raw_field, save)) 789 setattr(cls, 'move_%s_file' % self.name, lambda instance, raw_field, save=True: instance._save_FIELD_file(self, None, raw_field, save)) 789 790 dispatcher.connect(self.delete_file, signal=signals.post_delete, sender=cls) 790 791 791 792 def delete_file(self, instance): … … 808 809 if new_data.get(upload_field_name, False): 809 810 func = getattr(new_object, 'save_%s_file' % self.name) 810 811 if rel: 811 func(new_data[upload_field_name][0] ["filename"], new_data[upload_field_name][0]["content"], save)812 func(new_data[upload_field_name][0].file_name, new_data[upload_field_name][0], save) 812 813 else: 813 func(new_data[upload_field_name] ["filename"], new_data[upload_field_name]["content"], save)814 func(new_data[upload_field_name].file_name, new_data[upload_field_name], save) 814 815 815 816 def get_directory_name(self): 816 817 return os.path.normpath(force_unicode(datetime.datetime.now().strftime(smart_str(self.upload_to)))) … … 823 824 def save_form_data(self, instance, data): 824 825 from django.newforms.fields import UploadedFile 825 826 if data and isinstance(data, UploadedFile): 826 getattr(instance, "save_%s_file" % self.name)(data.filename, data. content, save=False)827 getattr(instance, "save_%s_file" % self.name)(data.filename, data.data, save=False) 827 828 828 829 def formfield(self, **kwargs): 829 830 defaults = {'form_class': forms.FileField} -
django/oldforms/__init__.py
680 680 self.field_name, self.is_required = field_name, is_required 681 681 self.validator_list = [self.isNonEmptyFile] + validator_list 682 682 683 def isNonEmptyFile(self, field_data, all_data):684 try:685 content = field_data['content']686 except TypeError:687 raise validators.CriticalValidationError, ugettext("No file was submitted. Check the encoding type on the form.")688 if not content:683 def isNonEmptyFile(self, new_data, all_data): 684 if hasattr(new_data, 'upload_errors'): 685 upload_errors = new_data.upload_errors() 686 if upload_errors: 687 raise validators.CriticalValidationError, upload_errors 688 if not new_data.file_size: 689 689 raise validators.CriticalValidationError, ugettext("The submitted file is empty.") 690 690 691 691 def render(self, data): 692 692 return mark_safe(u'<input type="file" id="%s" class="v%s" name="%s" />' % \ 693 693 (self.get_id(), self.__class__.__name__, self.field_name)) 694 694 695 def prepare(self, new_data): 696 if hasattr(new_data, 'upload_errors'): 697 upload_errors = new_data.upload_errors() 698 new_data[self.field_name] = { '_file_upload_error': upload_errors } 699 695 700 def html2python(data): 696 701 if data is None: 697 702 raise EmptyValue -
django/core/handlers/wsgi.py
78 78 self.path = force_unicode(environ['PATH_INFO']) 79 79 self.META = environ 80 80 self.method = environ['REQUEST_METHOD'].upper() 81 self._upload_handlers = [] 81 82 82 83 def __repr__(self): 83 84 # Since this is called as part of error handling, we need to be very … … 112 113 # Populates self._post and self._files 113 114 if self.method == 'POST': 114 115 if self.environ.get('CONTENT_TYPE', '').startswith('multipart'): 115 header_dict = dict([(k, v) for k, v in self.environ.items() if k.startswith('HTTP_')]) 116 header_dict['Content-Type'] = self.environ.get('CONTENT_TYPE', '') 117 self._post, self._files = http.parse_file_upload(header_dict, self.raw_post_data) 116 self._raw_post_data = '' 117 self._post, self._files = self.parse_file_upload(self.META, self.environ['wsgi.input']) 118 118 else: 119 119 self._post, self._files = http.QueryDict(self.raw_post_data, encoding=self._encoding), datastructures.MultiValueDict() 120 120 else: -
django/core/handlers/modpython.py
16 16 def __init__(self, req): 17 17 self._req = req 18 18 self.path = force_unicode(req.uri) 19 self._upload_handlers = [] 19 20 20 21 def __repr__(self): 21 22 # Since this is called as part of error handling, we need to be very … … 53 54 def _load_post_and_files(self): 54 55 "Populates self._post and self._files" 55 56 if 'content-type' in self._req.headers_in and self._req.headers_in['content-type'].startswith('multipart'): 56 self._post, self._files = http.parse_file_upload(self._req.headers_in, self.raw_post_data) 57 self._raw_post_data = '' 58 self._post, self._files = self.parse_file_upload(self.META, self._req) 57 59 else: 58 60 self._post, self._files = http.QueryDict(self.raw_post_data, encoding=self._encoding), datastructures.MultiValueDict() 59 61 -
django/core/files/filelocks.py
1 """ 2 Locking portability based partially on example by 3 Jonathan Feignberg <jdf@pobox.com> in python cookbook. 4 5 Example Usage:: 6 7 from django.utils import file_locks 8 9 f = open('./file', 'wb') 10 11 file_locks.lock(f, file_locks.LOCK_EX) 12 f.write('Django') 13 f.close() 14 """ 15 16 __all__ = ('LOCK_EX','LOCK_SH','LOCK_NB','lock','unlock') 17 18 system_type = None 19 20 try: 21 import win32con 22 import win32file 23 import pywintypes 24 LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK 25 LOCK_SH = 0 26 LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY 27 __overlapped = pywintypes.OVERLAPPED() 28 system_type = 'nt' 29 except (ImportError, AttributeError): 30 pass 31 32 try: 33 import fcntl 34 LOCK_EX = fcntl.LOCK_EX 35 LOCK_SH = fcntl.LOCK_SH 36 LOCK_NB = fcntl.LOCK_NB 37 system_type = 'posix' 38 except (ImportError, AttributeError): 39 pass 40 41 42 43 if system_type == 'nt': 44 def lock(file, flags): 45 hfile = win32file._get_osfhandle(file.fileno()) 46 win32file.LockFileEx(hfile, flags, 0, -0x10000, __overlapped) 47 48 def unlock(file): 49 hfile = win32file._get_osfhandle(file.fileno()) 50 win32file.UnlockFileEx(hfile, 0, -0x10000, __overlapped) 51 52 elif system_type =='posix': 53 def lock(file, flags): 54 fcntl.flock(file.fileno(), flags) 55 56 def unlock(file): 57 fcntl.flock(file.fileno(), fcntl.LOCK_UN) 58 59 else: 60 # File locking is not supported. 61 LOCK_EX = LOCK_SH = LOCK_NB = None 62 63 # Dummy functions that don't do anything. 64 def lock(file, flags): 65 pass 66 67 def unlock(file): 68 pass -
django/core/files/uploadedfile.py
1 """ 2 The uploaded file objects for Django. 3 This contains the base UploadedFile and the TemporaryUploadedFile 4 derived class. 5 """ 6 7 __all__ = ('UploadedFile', 'TemporaryUploadedFile', 'InMemoryUploadedFile') 8 9 class UploadedFile(object): 10 """ 11 The UploadedFile object behaves somewhat like a file 12 object and represents some data that the user submitted 13 and is stored in some form. 14 """ 15 DEFAULT_CHUNK_SIZE = 64 * 2**10 16 17 def __init__(self): 18 self.file_size = None 19 self.file_name = None 20 self.content_type = None 21 self.charset = None 22 pass 23 24 def file_size(self): 25 return self.file_size 26 27 def chunk(self, chunk_size=None): 28 """ 29 Read the file to generate chunks of chunk_size bytes. 30 """ 31 if not chunk_size: 32 chunk_size = UploadedFile.DEFAULT_CHUNK_SIZE 33 34 if hasattr(self, 'seek'): 35 self.seek(0) 36 # Assume the pointer is at zero... 37 counter = self.file_size() 38 39 while counter > 0: 40 yield self.read(chunk_size) 41 counter -= chunk_size 42 43 44 def multiple_chunks(self, chunk_size=None): 45 """ 46 Return True if you can expect multiple chunks, False otherwise. 47 Note: If a particular file representation is in memory, then 48 override this to return False. 49 """ 50 if not chunk_size: 51 chunk_size = UploadedFile.DEFAULT_CHUNK_SIZE 52 return self.file_size() < chunk_size 53 54 55 def read(self, num_bytes=None): 56 """ 57 Read from the file in whatever representation it has. 58 """ 59 raise NotImplementedError() 60 61 def open(self): 62 """ 63 Open the file, if one needs to. 64 """ 65 pass 66 67 68 def close(self): 69 """ 70 Close the file, if one needs to. 71 """ 72 pass 73 74 def __getitem__(self, key): 75 """ 76 This maintains backwards compatibility. 77 """ 78 import warnings 79 warnings.warn("The dictionary access of uploaded file objects is deprecated. Use the new object interface instead.", DeprecationWarning) 80 # Dictionary to translate labels 81 # for backwards compatbility. 82 # Should be removed at some point. 83 backwards_translate = { 84 'filename': 'file_name', 85 'content-type': 'content_type', 86 } 87 88 if key == 'content': 89 return self.read() 90 else: 91 return getattr(self, backwards_translate.get(key, key)) 92 93 def __repr__(self): 94 """ 95 This representation could be anything and can be overridden. 96 This is mostly done to make it look somewhat useful. 97 """ 98 _dict = { 99 'file_name': self.file_name, 100 'content_type': self.content_type, 101 'content': '<omitted>', 102 } 103 return repr(_dict) 104 105 106 class TemporaryUploadedFile(UploadedFile): 107 """ 108 Upload a file to a temporary file. 109 """ 110 111 def __init__(self, file, file_name, content_type, file_size, charset): 112 self.file = file 113 self.file_name = file_name 114 self.path = file.name 115 self.content_type = content_type 116 self.file_size = file_size 117 self.charset = charset 118 self.file.seek(0) 119 120 def temporary_file_path(self): 121 """ 122 Return the full path of this file. 123 """ 124 return self.path 125 126 def read(self, *args, **kwargs): 127 return self.file.read(*args, **kwargs) 128 129 def open(self): 130 """ 131 Assume the person meant to seek. 132 """ 133 self.seek(0) 134 135 def seek(self, *args, **kwargs): 136 self.file.seek(*args, **kwargs) 137 138 139 class InMemoryUploadedFile(UploadedFile): 140 """ 141 Upload a file into memory. 142 """ 143 def __init__(self, file, field_name, file_name, content_type, charset): 144 self.file = file 145 self.field_name = field_name 146 self.file_name = file_name 147 self.content_type = content_type 148 self.charset = charset 149 self.file.seek(0) 150 151 def seek(self, *args, **kwargs): 152 self.file.seek(*args, **kwargs) 153 154 def open(self): 155 self.seek(0) 156 157 def read(self, *args, **kwargs): 158 return self.file.read(*args, **kwargs) 159 160 def chunk(self, chunk_size=None): 161 """ 162 Return the entirety of the data regardless. 163 """ 164 self.file.seek(0) 165 return self.read() 166 167 def multiple_chunks(self, chunk_size=None): 168 """ 169 Since it's in memory, we'll never have multiple chunks. 170 """ 171 return False 172 173 174 class SimpleUploadedFile(InMemoryUploadedFile): 175 """ 176 A simple representation of a file, which 177 just has content, size, and a name. 178 """ 179 def __init__(self, name, content, content_type='text/plain'): 180 try: 181 from cStringIO import StringIO 182 except ImportError: 183 from StringIO import StringIO 184 self.file = StringIO(content or '') 185 self.file_name = name 186 self.field_name = None 187 self.file_size = len(content or '') 188 self.content_type = content_type 189 self.charset = None 190 self.file.seek(0) 191 -
django/core/files/__init__.py
1 -
django/core/files/fileuploadhandler.py
1 """ A fileuploadhandler base and default subclass for handling file uploads. 2 """ 3 import os 4 try: 5 from cStringIO import StringIO 6 except ImportError: 7 from StringIO import StringIO 8 9 from django.utils.encoding import force_unicode 10 from django.utils.datastructures import MultiValueDict 11 12 from django.core.files.uploadedfile import TemporaryUploadedFile, InMemoryUploadedFile 13 14 __all__ = ('UploadFileException','StopUpload', 'SkipFile', 15 'FileUploadHandler', 'TemporaryFileUploadHandler', 16 'MemoryFileUploadHandler') 17 18 19 class UploadFileException(Exception): 20 """ Any error having to do with Uploading Files. """ 21 pass 22 23 class StopUpload(UploadFileException): 24 """ This exception is raised when an upload must abort. """ 25 pass 26 27 class SkipFile(UploadFileException): 28 """ This exception is raised when a file needs to be skipped. """ 29 pass 30 31 32 class FileUploadHandler(object): 33 """ FileUploadHandler will take data and handle file uploads 34 in a streamed fashion. 35 """ 36 chunk_size = 64 * 2 ** 10 #: The default chunk size is 64 KB. 37 38 def __init__(self): 39 " Initialize some local variables. " 40 self.file_name = None 41 self.content_type = None 42 self.content_length = None 43 self.charset = None 44 45 def handle_raw_input(self, input_data, META, content_length, boundary, encoding=None): 46 """ 47 Handle the raw input from the client. 48 Parameters: 49 *input_data* -- An object that supports reading via .read(). 50 *content_length* -- The (integer) value of the Content-Length header from the client. 51 *boundary* -- The boundary from the Content-Type header. Be sure to prepend two '--'. 52 """ 53 pass 54 55 def new_file(self, field_name, file_name, content_type, content_length, charset=None): 56 """ 57 Signal that a new file has been started. 58 59 Warning: Do not trust content_length, if you get it at all. 60 """ 61 self.field_name = field_name 62 self.file_name = file_name 63 self.content_type = content_type 64 self.content_length = content_length 65 self.charset = charset 66 67 def receive_data_chunk(self, raw_data, start, stop): 68 """ 69 Receive data from the streamed upload parser. 70 Start and stop are the positions in the file. 71 This equality should always be true:: 72 len(raw_data) = stop - start 73 """ 74 raise NotImplementedError() 75 76 def file_complete(self, file_size): 77 """ 78 Signal that a file has completed. 79 File size corresponds to the actual size accumulated 80 by all the chunks. 81 82 This should return a valid UploadedFile object. 83 """ 84 raise NotImplementedError() 85 86 def upload_complete(self): 87 """ 88 Signal that the upload is complete. 89 Do any cleanup that is necessary for this handler. 90 """ 91 pass 92 93 94 95 class TemporaryFileUploadHandler(FileUploadHandler): 96 """ 97 Upload the streaming data into a temporary file. 98 """ 99 def __init__(self, *args, **kwargs): 100 """ Import settings for later. """ 101 super(TemporaryFileUploadHandler, self).__init__(*args, **kwargs) 102 global settings 103 from django.conf import settings 104 105 def new_file(self, file_name, *args, **kwargs): 106 """ 107 Create the file object to append to as data is coming in. 108 """ 109 super(TemporaryFileUploadHandler, self).new_file(file_name, *args, **kwargs) 110 self.file = TemporaryFile(settings.FILE_UPLOAD_TEMP_DIR) 111 self.write = self.file.write 112 113 def receive_data_chunk(self, raw_data, start, stop): 114 """ 115 Once we get the data, we will save it to our file. 116 """ 117 self.write(raw_data) 118 119 def file_complete(self, file_size): 120 """ 121 Signal that a file has completed. 122 File size corresponds to the actual size accumulated 123 by all the chunks. 124 125 This should return a valid UploadedFile object. 126 """ 127 self.file.seek(0) 128 return TemporaryUploadedFile(self.file, self.file_name, 129 self.content_type, file_size, 130 self.charset) 131 132 133 class TemporaryFile(object): 134 """ 135 A temporary file that tries to delete itself when garbage collected. 136 """ 137 def __init__(self, dir): 138 import tempfile 139 if not dir: 140 dir = tempfile.gettempdir() 141 try: 142 (fd, name) = tempfile.mkstemp(suffix='.upload', dir=dir) 143 self.file = os.fdopen(fd, 'w+b') 144 except (OSError, IOError): 145 raise OSError, "Could not create temporary file for uploading, have you set settings.FILE_UPLOAD_TEMP_DIR correctly?" 146 self.name = name 147 148 def __getattr__(self, name): 149 a = getattr(self.__dict__['file'], name) 150 if type(a) != type(0): 151 setattr(self, name, a) 152 return a 153 154 def __del__(self): 155 try: 156 os.unlink(self.name) 157 except OSError: 158 pass 159 160 161 class MemoryFileUploadHandler(FileUploadHandler): 162 """ 163 The MemoryFileUploadHandler will place the data directly into memory. 164 """ 165 166 def __init__(self): 167 pass 168 169 170 def handle_raw_input(self, input_data, META, content_length, boundary, encoding=None): 171 """ 172 Parse the input data in-memory. 173 """ 174 if content_length > 2621440: 175 # If the post is greater than 2.5 MB, do nothing. 176 return 177 178 from django.http import QueryDict 179 import email, email.Message 180 from cgi import parse_header 181 182 ##### 183 # Get the headers from the META information. 184 headers = [] 185 if 'HTTP_CONTENT_TYPE' not in META: 186 headers.append('Content-Type: %s' % (META.get('CONTENT_TYPE', ''))) 187 188 if 'HTTP_CONTENT_LENGTH' not in META: 189 headers.append('Content-Length: %s' % (META.get('CONTENT_LENGTH', '0'))) 190 191 for key, value in META.items(): 192 if key.startswith('HTTP_'): 193 headers.append('%s: %s' % (key[5:].replace('_','-').title(), value)) 194 195 raw_message = '\r\n'.join(headers) 196 raw_message += '\r\n\r\n' + input_data.read() 197 198 msg = email.message_from_string(raw_message) 199 POST = QueryDict('', mutable=True) 200 FILES = MultiValueDict() 201 for submessage in msg.get_payload(): 202 if submessage and isinstance(submessage, email.Message.Message): 203 name_dict = parse_header(submessage['Content-Disposition'])[1] 204 field_name = force_unicode(name_dict['name'], encoding, errors='replace') 205 206 if 'filename' in name_dict: 207 assert not isinstance(submessage.get_payload(), list), "Nested MIME messages are not supported" 208 if not name_dict['filename'].strip(): 209 continue 210 211 filename = force_unicode(name_dict['filename'][name_dict['filename'].rfind("\\")+1:], 212 encoding, errors='replace') 213 content_type = 'Content-Type' in submessage and submessage['Content-Type'] or None 214 215 file_obj = InMemoryUploadedFile(StringIO(submessage.get_payload()), 216 field_name, filename, content_type, None) 217 218 FILES.appendlist(field_name, file_obj) 219 else: 220 content = force_unicode(submessage.get_payload(), encoding, errors='replace') 221 POST.appendlist(field_name, content) 222 223 return POST, FILES 224 225 226 def new_file(self, field_name, file_name, content_type, content_length, charset): 227 """ 228 Do Nothing. 229 """ 230 return 231 232 def receive_data_chunk(self, raw_data, start, stop): 233 """ 234 Do nothing. 235 """ 236 return raw_data 237 238 def file_complete(self, file_size): 239 """ 240 Do nothing. 241 """ 242 return 243 -
django/core/files/filemove.py
1 import os 2 3 __all__ = ('file_move_safe',) 4 5 try: 6 import shutil 7 file_move = shutil.move 8 except ImportError: 9 file_move = os.rename 10 11 def file_move_safe(old_file_name, new_file_name, chunk_size = 1024*64, allow_overwrite=False): 12 """ 13 Moves a file from one location to another in the safest way possible. 14 15 First, it tries using shutils.move, which is OS-dependent but doesn't 16 break with change of filesystems. Then it tries os.rename, which will 17 break if it encounters a change in filesystems. Lastly, it streams 18 it manually from one file to another in python. 19 20 Without ``allow_overwrite``, if the destination file exists, the 21 file will raise an IOError. 22 """ 23 24 from django.core.files import filelocks 25 26 if old_file_name == new_file_name: 27 # No file moving takes place. 28 return 29 30 if not allow_overwrite and os.path.exists(new_file_name): 31 raise IOError, "Django does not allow overwriting files." 32 33 try: 34 file_move(old_file_name, new_file_name) 35 return 36 except OSError: # moving to another filesystem 37 pass 38 39 new_file = open(new_file_name, 'wb') 40 # exclusive lock 41 filelocks.lock(new_file, filelocks.LOCK_EX) 42 old_file = open(old_file_name, 'rb') 43 current_chunk = None 44 45 while current_chunk != '': 46 current_chunk = old_file.read(chunk_size) 47 new_file.write(current_chunk) 48 49 new_file.close() 50 old_file.close() 51 52 os.remove(old_file_name) -
django/newforms/fields.py
416 416 417 417 class UploadedFile(StrAndUnicode): 418 418 "A wrapper for files uploaded in a FileField" 419 def __init__(self, filename, content):419 def __init__(self, filename, data): 420 420 self.filename = filename 421 self. content = content421 self.data = data 422 422 423 423 def __unicode__(self): 424 424 """ … … 445 445 elif not data and initial: 446 446 return initial 447 447 try: 448 f = UploadedFile(data ['filename'], data['content'])449 except TypeError:448 f = UploadedFile(data.file_name, data) 449 except (TypeError, AttributeError): 450 450 raise ValidationError(self.error_messages['invalid']) 451 except KeyError: 452 raise ValidationError(self.error_messages['missing']) 453 if not f.content: 451 if not f.data.file_size: 454 452 raise ValidationError(self.error_messages['empty']) 455 453 return f 456 454 … … 470 468 elif not data and initial: 471 469 return initial 472 470 from PIL import Image 473 from cStringIO import StringIO 471 472 # We need to get the file, it either has a path 473 # or we have to read it all into memory... 474 if hasattr(data, 'temporary_file_path'): 475 file = data.temporary_file_path() 476 else: 477 try: 478 from cStringIO import StringIO 479 except ImportError: 480 from StringIO import StringIO 481 file = StringIO(data.read()) 482 474 483 try: 475 484 # load() is the only method that can spot a truncated JPEG, 476 485 # but it cannot be called sanely after verify() 477 trial_image = Image.open( StringIO(f.content))486 trial_image = Image.open(file) 478 487 trial_image.load() 479 488 # verify() is the only method that can spot a corrupt PNG, 480 489 # but it must be called immediately after the constructor 481 trial_image = Image.open( StringIO(f.content))490 trial_image = Image.open(file) 482 491 trial_image.verify() 483 492 except Exception: # Python Imaging Library doesn't recognize it as an image 484 493 raise ValidationError(self.error_messages['invalid_image']) -
tests/modeltests/model_forms/models.py
75 75 __test__ = {'API_TESTS': """ 76 76 >>> from django import newforms as forms 77 77 >>> from django.newforms.models import ModelForm 78 >>> from django.core.files.uploadedfile import SimpleUploadedFile 78 79 79 80 The bare bones, absolutely nothing custom, basic case. 80 81 … … 792 793 793 794 # Upload a file and ensure it all works as expected. 794 795 795 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': {'filename': 'test1.txt', 'content': 'hello world'}})796 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test1.txt', 'hello world')}) 796 797 >>> f.is_valid() 797 798 True 798 799 >>> type(f.cleaned_data['file']) … … 819 820 820 821 # Override the file by uploading a new one. 821 822 822 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': {'filename': 'test2.txt', 'content': 'hello world'}}, instance=instance)823 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test2.txt', 'hello world')}, instance=instance) 823 824 >>> f.is_valid() 824 825 True 825 826 >>> instance = f.save() … … 838 839 >>> instance.file 839 840 '' 840 841 841 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': {'filename': 'test3.txt', 'content': 'hello world'}}, instance=instance)842 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test3.txt', 'hello world')}, instance=instance) 842 843 >>> f.is_valid() 843 844 True 844 845 >>> instance = f.save() … … 858 859 859 860 >>> image_data = open(os.path.join(os.path.dirname(__file__), "test.png")).read() 860 861 861 >>> f = ImageFileForm(data={'description': u'An image'}, files={'image': {'filename': 'test.png', 'content': image_data}})862 >>> f = ImageFileForm(data={'description': u'An image'}, files={'image': SimpleUploadedFile('test.png', image_data)}) 862 863 >>> f.is_valid() 863 864 True 864 865 >>> type(f.cleaned_data['image']) … … 885 886 886 887 # Override the file by uploading a new one. 887 888 888 >>> f = ImageFileForm(data={'description': u'Changed it'}, files={'image': {'filename': 'test2.png', 'content': image_data}}, instance=instance)889 >>> f = ImageFileForm(data={'description': u'Changed it'}, files={'image': SimpleUploadedFile('test2.png', image_data)}, instance=instance) 889 890 >>> f.is_valid() 890 891 True 891 892 >>> instance = f.save() … … 904 905 >>> instance.image 905 906 '' 906 907 907 >>> f = ImageFileForm(data={'description': u'And a final one'}, files={'image': {'filename': 'test3.png', 'content': image_data}}, instance=instance)908 >>> f = ImageFileForm(data={'description': u'And a final one'}, files={'image': SimpleUploadedFile('test3.png', image_data)}, instance=instance) 908 909 >>> f.is_valid() 909 910 True 910 911 >>> instance = f.save() -
tests/regressiontests/bug639/tests.py
9 9 from regressiontests.bug639.models import Photo 10 10 from django.http import QueryDict 11 11 from django.utils.datastructures import MultiValueDict 12 from django.core.files.uploadedfile import SimpleUploadedFile 12 13 13 14 class Bug639Test(unittest.TestCase): 14 15 … … 21 22 22 23 # Fake a request query dict with the file 23 24 qd = QueryDict("title=Testing&image=", mutable=True) 24 qd["image_file"] = { 25 "filename" : "test.jpg", 26 "content-type" : "image/jpeg", 27 "content" : img 28 } 25 qd["image_file"] = SimpleUploadedFile('test.jpg', img, 'image/jpeg') 29 26 30 27 manip = Photo.AddManipulator() 31 28 manip.do_html2python(qd) … … 39 36 Make sure to delete the "uploaded" file to avoid clogging /tmp. 40 37 """ 41 38 p = Photo.objects.get() 42 os.unlink(p.get_image_filename()) 43 No newline at end of file 39 os.unlink(p.get_image_filename()) -
tests/regressiontests/forms/error_messages.py
1 1 # -*- coding: utf-8 -*- 2 2 tests = r""" 3 3 >>> from django.newforms import * 4 >>> from django.core.files.uploadedfile import SimpleUploadedFile 4 5 5 6 # CharField ################################################################### 6 7 … … 214 215 Traceback (most recent call last): 215 216 ... 216 217 ValidationError: [u'INVALID'] 217 >>> f.clean( {})218 >>> f.clean(SimpleUploadedFile('name', None)) 218 219 Traceback (most recent call last): 219 220 ... 220 ValidationError: [u' MISSING']221 >>> f.clean( {'filename': 'name', 'content':''})221 ValidationError: [u'EMPTY FILE'] 222 >>> f.clean(SimpleUploadedFile('name', '')) 222 223 Traceback (most recent call last): 223 224 ... 224 225 ValidationError: [u'EMPTY FILE'] -
tests/regressiontests/forms/fields.py
2 2 tests = r""" 3 3 >>> from django.newforms import * 4 4 >>> from django.newforms.widgets import RadioFieldRenderer 5 >>> from django.core.files.uploadedfile import SimpleUploadedFile 5 6 >>> import datetime 6 7 >>> import time 7 8 >>> import re … … 773 774 >>> f.clean({}) 774 775 Traceback (most recent call last): 775 776 ... 776 ValidationError: [u'No file was submitted. ']777 ValidationError: [u'No file was submitted. Check the encoding type on the form.'] 777 778 778 779 >>> f.clean({}, '') 779 780 Traceback (most recent call last): 780 781 ... 781 ValidationError: [u'No file was submitted. ']782 ValidationError: [u'No file was submitted. Check the encoding type on the form.'] 782 783 783 784 >>> f.clean({}, 'files/test3.pdf') 784 785 'files/test3.pdf' … … 788 789 ... 789 790 ValidationError: [u'No file was submitted. Check the encoding type on the form.'] 790 791 791 >>> f.clean( {'filename': 'name', 'content': None})792 >>> f.clean(SimpleUploadedFile('name', None)) 792 793 Traceback (most recent call last): 793 794 ... 794 795 ValidationError: [u'The submitted file is empty.'] 795 796 796 >>> f.clean( {'filename': 'name', 'content': ''})797 >>> f.clean(SimpleUploadedFile('name', '')) 797 798 Traceback (most recent call last): 798 799 ... 799 800 ValidationError: [u'The submitted file is empty.'] 800 801 801 >>> type(f.clean( {'filename': 'name', 'content': 'Some File Content'}))802 >>> type(f.clean(SimpleUploadedFile('name', 'Some File Content'))) 802 803 <class 'django.newforms.fields.UploadedFile'> 803 804 804 >>> type(f.clean( {'filename': 'name', 'content': 'Some File Content'}, 'files/test4.pdf'))805 >>> type(f.clean(SimpleUploadedFile('name', 'Some File Content'), 'files/test4.pdf')) 805 806 <class 'django.newforms.fields.UploadedFile'> 806 807 807 808 # URLField ################################################################## -
tests/regressiontests/forms/forms.py
1 1 # -*- coding: utf-8 -*- 2 2 tests = r""" 3 3 >>> from django.newforms import * 4 >>> from django.core.files.uploadedfile import SimpleUploadedFile 4 5 >>> import datetime 5 6 >>> import time 6 7 >>> import re … … 1465 1466 >>> print f 1466 1467 <tr><th>File1:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="file" name="file1" /></td></tr> 1467 1468 1468 >>> f = FileForm(data={}, files={'file1': {'filename': 'name', 'content':''}}, auto_id=False)1469 >>> f = FileForm(data={}, files={'file1': SimpleUploadedFile('name', '')}, auto_id=False) 1469 1470 >>> print f 1470 1471 <tr><th>File1:</th><td><ul class="errorlist"><li>The submitted file is empty.</li></ul><input type="file" name="file1" /></td></tr> 1471 1472 … … 1473 1474 >>> print f 1474 1475 <tr><th>File1:</th><td><ul class="errorlist"><li>No file was submitted. Check the encoding type on the form.</li></ul><input type="file" name="file1" /></td></tr> 1475 1476 1476 >>> f = FileForm(data={}, files={'file1': {'filename': 'name', 'content':'some content'}}, auto_id=False)1477 >>> f = FileForm(data={}, files={'file1': SimpleUploadedFile('name', 'some content')}, auto_id=False) 1477 1478 >>> print f 1478 1479 <tr><th>File1:</th><td><input type="file" name="file1" /></td></tr> 1479 1480 >>> f.is_valid() -
tests/regressiontests/test_client_regress/views.py
1 1 from django.contrib.auth.decorators import login_required 2 2 from django.http import HttpResponse, HttpResponseRedirect, HttpResponseServerError 3 import sha 3 4 4 5 def no_template_view(request): 5 6 "A simple view that expects a GET request, and returns a rendered template" … … 10 11 Check that a file upload can be updated into the POST dictionary without 11 12 going pear-shaped. 12 13 """ 14 from django.core.files.uploadedfile import UploadedFile 13 15 form_data = request.POST.copy() 14 16 form_data.update(request.FILES) 15 if isinstance(form_data['file_field'], dict) and isinstance(form_data['name'], unicode):17 if isinstance(form_data['file_field'], UploadedFile) and isinstance(form_data['name'], unicode): 16 18 return HttpResponse('') 17 19 else: 18 20 return HttpResponseServerError() 19 21 22 def file_upload_view_verify(request): 23 """ 24 Use the sha digest hash to verify the uploaded contents. 25 """ 26 from django.core.files.uploadedfile import UploadedFile 27 form_data = request.POST.copy() 28 form_data.update(request.FILES) 29 for key, value in form_data.items(): 30 if key.endswith('_hash'): 31 continue 32 if key + '_hash' not in form_data: 33 continue 34 submitted_hash = form_data[key + '_hash'] 35 if isinstance(value, UploadedFile): 36 new_hash = sha.new(value.read()).hexdigest() 37 else: 38 new_hash = sha.new(value).hexdigest() 39 if new_hash != submitted_hash: 40 return HttpResponseServerError() 41 42 return HttpResponse('') 43 20 44 def get_view(request): 21 45 "A simple login protected view" 22 46 return HttpResponse("Hello world") … … 37 61 def login_protected_redirect_view(request): 38 62 "A view that redirects all requests to the GET view" 39 63 return HttpResponseRedirect('/test_client_regress/get_view/') 40 login_protected_redirect_view = login_required(login_protected_redirect_view) 41 No newline at end of file 64 login_protected_redirect_view = login_required(login_protected_redirect_view) -
tests/regressiontests/test_client_regress/models.py
5 5 from django.test import Client, TestCase 6 6 from django.core.urlresolvers import reverse 7 7 import os 8 import sha 8 9 9 10 class AssertContainsTests(TestCase): 10 11 def test_contains(self): … … 243 244 response = self.client.post('/test_client_regress/file_upload/', post_data) 244 245 self.assertEqual(response.status_code, 200) 245 246 247 def test_large_upload(self): 248 import tempfile 249 dir = tempfile.gettempdir() 250 251 (fd, name1) = tempfile.mkstemp(suffix='.file1', dir=dir) 252 file1 = os.fdopen(fd, 'w+b') 253 file1.write('a' * (2 ** 21)) 254 file1.seek(0) 255 256 (fd, name2) = tempfile.mkstemp(suffix='.file2', dir=dir) 257 file2 = os.fdopen(fd, 'w+b') 258 file2.write('a' * (10 * 2 ** 20)) 259 file2.seek(0) 260 261 post_data = { 262 'name': 'Ringo', 263 'file_field1': file1, 264 'file_field2': file2, 265 } 266 267 for key in post_data.keys(): 268 try: 269 post_data[key + '_hash'] = sha.new(post_data[key].read()).hexdigest() 270 post_data[key].seek(0) 271 except AttributeError: 272 post_data[key + '_hash'] = sha.new(post_data[key]).hexdigest() 273 274 response = self.client.post('/test_client_regress/file_upload_verify/', post_data) 275 276 for name in (name1, name2): 277 try: 278 os.unlink(name) 279 except: 280 pass 281 282 self.assertEqual(response.status_code, 200) 283 284 246 285 class LoginTests(TestCase): 247 286 fixtures = ['testdata'] 248 287 -
tests/regressiontests/test_client_regress/urls.py
4 4 urlpatterns = patterns('', 5 5 (r'^no_template_view/$', views.no_template_view), 6 6 (r'^file_upload/$', views.file_upload_view), 7 (r'^file_upload_verify/$', views.file_upload_view_verify), 7 8 (r'^get_view/$', views.get_view), 8 9 url(r'^arg_view/(?P<name>.+)/$', views.view_with_argument, name='arg_view'), 9 10 (r'^login_protected_redirect_view/$', views.login_protected_redirect_view) -
AUTHORS
58 58 Jökull Sólberg Auðunsson <jokullsolberg@gmail.com> 59 59 Arthur <avandorp@gmail.com> 60 60 David Avsajanishvili <avsd05@gmail.com> 61 axiak@mit.edu61 Mike Axiak <axiak@mit.edu> 62 62 Niran Babalola <niran@niran.org> 63 63 Morten Bagai <m@bagai.com> 64 64 Mikaël Barbero <mikael.barbero nospam at nospam free.fr> … … 135 135 Marc Fargas <telenieko@telenieko.com> 136 136 Szilveszter Farkas <szilveszter.farkas@gmail.com> 137 137 favo@exoweb.net 138 fdr <drfarina@gmail.com> 138 139 Dmitri Fedortchenko <zeraien@gmail.com> 139 140 Bill Fenner <fenner@gmail.com> 140 141 Stefane Fermgier <sf@fermigier.com> -
docs/upload_handling.txt
1 =============== 2 Upload Handlers 3 =============== 4 5 Upload handlers are components to modify how Django handles file uploads. Through the upload handling framework, Django allows complete control over what happens during and after uploads. 6 7 An upload handler can do many different things with the uploaded data. For instance, one can stream your uploaded data to disk, while another can generate the data to render a Progress Bar for your uploads. 8 9 There are two pieces to the Django upload handling: the upload handler and the uploaded file. These are both represented by python classes -- ``FileUploadHandler`` and ``UploadedFile`` respectively. Throughout the lifetime of the upload process, Django will call on the upload handler to handle the upload, while the upload handler is expected to return an ``UploadedFile`` object at the end of a file's upload. 10 11 Adding Upload Handlers to your Request 12 ====================================== 13 14 To use an upload handler, you have to register it in your request. The request object contains a list of handlers (``request.upload_handlers``) that will be called in order. To append a handler to the list, you would append to it like any other list. For example, suppose you had an ``ProgressBarUploadHandler`` class. To append it to your upload handlers you would write:: 15 16 request.upload_handlers.append(ProgressBarUploadHandler()) 17 18 However, since the progress bar handler would probably need to run before the other handlers get a chance, you'd probably want to insert it. That is, you'd write:: 19 20 request.upload_handlers.insert(0, ProgressBarUploadHandler()) 21 22 If you want to replace the upload handlers completely, you can just assign a new list:: 23 24 request.upload_handlers = [ProgressBarUploadHandler()] 25 26 And all Django will do is keep a progress, but do nothing with the file itself! One more note: After the upload has completed, you are no longer allowed to assign or modify this list, and Django will raise an error if you try to modify this list after an upload. 27 28 Writing a File Upload Handler 29 ============================= 30 31 All file upload handlers are subclasses of ``FileUploadHandler``, found in ``django.core.files.fileuploadhandler``. To create your handler, you need to define the required methods followed by the methods that make most sense to you: 32 33 chunk_size 34 ---------- 35 36 This is an integer attribute that specifies how large the chunks we should put into memory from the network are. The chunk sizes should be divisible by ``4`` and should not exceed ``2 ** 31 - 1`` in size. When there are multiple chunk sizes provided by multiple handlers, Django will use the smallest chunk size. 37 38 new_file 39 -------- 40 41 This signals that a new file is starting. You can initialize a file or whatever else is needed on a new file upload. 42 43 Interface: ``new_file(self, field_name, file_name, content_type, content_length, charset)`` 44 45 ``field_name`` is a string name of the field this was POSTed as. 46 47 ``file_name`` is the unicode filename that was provided by the browser. 48 49 ``content_type`` is the MIME type provided by the browser -- E.g. ``'image/jpeg'``. 50 51 ``content_length`` is the length of the image given by the browser if provided, ``None`` otherwise. 52 53 ``charset`` is the charset given by the browser if provided, ``None`` otherwise. 54 55 Returns: ``None`` if you want other handlers to get ``new_file`` called. Something nonzero if you don't want subsequent handlers to get a chance. 56 57 receive_data_chunk 58 ------------------ 59 *required* 60 61 This method is used to do something with the new chunk of data. For example: The ``TemporaryFileUploadHandler`` takes the data and writes it to disk. 62 63 Interface: ``receive_data_chunk(self, raw_data, start, stop)`` 64 65 ``raw_data`` is a byte string containing the uploaded data. 66 67 ``start`` is the byte number where the chunk starts. 68 69 ``stop`` is the byte number where the chunk stops. 70 71 Returns: ``None`` if you don't want the subsequent upload handlers to receive the data. Whatever else you return gets fed into the subsequent upload handlers' ``receive_data_chunk`` method. 72 73 Exceptions: If you raise a ``StopUpload`` or a ``SkipFile`` exception, the upload will abort or the file will be skipped respectively. 74 75 file_complete 76 ------------- 77 *required* 78 79 This method defines when a function has finished uploading and gets packaged into an ``UploadedFile`` object. 80 81 Interface: ``file_complete(self, file_size)`` 82 83 ``file_size`` is the number of bytes you have received for this file. 84 85 Returns: An ``UploadedFile`` object to set in the ``request.FILES`` dictionary. ``None`` if you want the subsequent upload handlers to get an ``UploadedFile`` object. 86 87 upload_complete 88 --------------- 89 90 This method defines when the entire upload has completed. 91 92 Interface: ``upload_complete(self)`` 93 94 handle_raw_input 95 ---------------- 96 97 This method allows the handler to completely override the parsing of the raw HTTP-layered input. 98 99 Interface: ``handle_raw_input(self, input_data, META, content_length, boundary, encoding)`` 100 101 ``input_data`` is a file-like object that supports the read operation. 102 103 ``META`` is the same object as ``request.META``. 104 105 ``content_length`` is the length of the data in ``input_data``. Don't read more than ``content_length`` bytes from ``input_data``. 106 107 ``boundary`` is the MIME boundary for this request. 108 109 ``encoding`` is the encoding of the request. 110 111 Returns: ``None`` if you want to go on to the next stage, ``(POST, FILES)`` if you want to return the new data structures suitable for the request directly. 112 113 Defining an Uploaded File 114 ========================= 115 116 All file upload handlers are subclasses of ``UploadedFile``, found in ``django.core.files.uploadedfile``. The uploaded file object is returned by the handler above in ``file_complete``. To create your own uploaded file class, you need to define the required methods followed by the methods that make most sense to you: 117 118 read 119 ---- 120 *required* 121 122 Interface: ``read(self, num_bytes=None)`` 123 124 Returns: A byte string of length ``num_bytes`` (or the size of the file if ``num_bytes`` is not supplied). 125 126 chunk 127 ----- 128 129 A generator to yield small chunks from the file. With the ``read()`` defined, the ``UploadedFile`` class already defines a ``chunk()`` method that's probably suitable. 130 131 Interface: ``chunk(self, chunk_size=None)`` 132 133 134 multiple_chunks 135 --------------- 136 137 Interface: ``multiple_chunks(self, chunk_size=None)`` 138 139 Returns: ``True`` or ``False`` depending on whether or not the user of this file can expect more than one chunk when calling ``chunk(self, chunk_size)``. If all the data is in memory, you should return ``False``. 140 141 temporary_file_path 142 ------------------- 143 144 If defined, this method should return the file path of the file on the operating system. This will let users move files rather than write to new files if the option is available. 145 146 Interface: ``temporary_file_path(self)`` 147 148 Returns: A file path in a file system on the local computer. -
docs/settings.txt
513 513 The character encoding used to decode any files read from disk. This includes 514 514 template files and initial SQL data files. 515 515 516 FILE_UPLOAD_TEMP_DIR 517 -------------------- 518 519 **New in Django development version** 520 521 Default: ``None`` 522 523 The directory to store data temporarily while uploading files. If ``None``, Django will use the standard temporary directory for the operating system. For example, this will default to '/tmp' on *nix-style operating systems. 524 516 525 FIXTURE_DIRS 517 526 ------------- 518 527