Ticket #2070: 2070_revision7484.diff
File 2070_revision7484.diff, 99.2 KB (added by , 17 years ago) |
---|
-
django/test/client.py
18 18 BOUNDARY = 'BoUnDaRyStRiNg' 19 19 MULTIPART_CONTENT = 'multipart/form-data; boundary=%s' % BOUNDARY 20 20 21 22 class FakePayload(object): 23 """ 24 A wrapper around StringIO that restricts what can be read, 25 since data from the network can't be seeked and cannot 26 be read outside of its content length (or else we hang). 27 """ 28 def __init__(self, content): 29 self.__content = StringIO(content) 30 self.__len = len(content) 31 32 def read(self, num_bytes=None): 33 if num_bytes is None: 34 num_bytes = self.__len or 1 35 assert self.__len >= num_bytes, "Cannot read more than the available bytes from the HTTP incoming data." 36 content = self.__content.read(num_bytes) 37 self.__len -= num_bytes 38 return content 39 40 21 41 class ClientHandler(BaseHandler): 22 42 """ 23 43 A HTTP Handler that can be used for testing purposes. … … 230 250 'CONTENT_TYPE': content_type, 231 251 'PATH_INFO': urllib.unquote(path), 232 252 'REQUEST_METHOD': 'POST', 233 'wsgi.input': StringIO(post_data),253 'wsgi.input': FakePayload(post_data), 234 254 } 235 255 r.update(extra) 236 256 -
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 from django.utils.text import unescape_entities 11 12 __all__ = ('MultiPartParser','MultiPartParserError','InputStreamExhausted') 13 14 class MultiPartParserError(Exception): 15 pass 16 17 class InputStreamExhausted(Exception): 18 """ No more reads are allowed from this device. """ 19 pass 20 21 22 class FieldType(object): 23 """ 24 Denotes what type of field a given section of the multipart 25 stream is. 26 27 The types can be one of ("FILE", "RAW", "FIELD") 28 """ 29 30 def __init__(self, name): 31 self.name = name 32 33 def __str__(self): 34 return self.name 35 36 RAW = FieldType('RAW') 37 FILE = FieldType('FILE') 38 FIELD = FieldType('FIELD') 39 40 41 class MultiPartParser(object): 42 """ 43 A rfc2388 multipart/form-data parser. 44 45 parse() reads the input stream in chunk_size chunks and returns a 46 tuple of (POST MultiValueDict, FILES MultiValueDict). If 47 file_upload_dir is defined files will be streamed to temporary 48 files in the specified directory. 49 """ 50 def __init__(self, META, input_data, upload_handlers, encoding=None): 51 """ 52 Initialize the MultiPartParser object. 53 54 *META* -- The standard META dictionary in Django request objects. 55 *input_data* -- The raw post data, as a bytestring. 56 *upload_handler* -- An object of type UploadHandler 57 that performs operations on the uploaded 58 data. 59 *encoding* -- The encoding with which to treat the incoming data. 60 """ 61 # Import cgi utilities for (near) future use. 62 global valid_boundary, settings 63 from django.conf import settings 64 from cgi import valid_boundary 65 66 # 67 # Content-Type should containt multipart and the boundary information. 68 # 69 70 content_type = META.get('HTTP_CONTENT_TYPE', META.get('CONTENT_TYPE', '')) 71 if not content_type.startswith('multipart/'): 72 raise MultiPartParserError('Invalid Content-Type: %s' % 73 content_type) 74 75 # Parse the header to get the boundary to split the parts. 76 ctypes, opts = parse_header(content_type) 77 boundary = opts.get('boundary') 78 if not boundary or not valid_boundary(boundary): 79 raise MultiPartParserError('Invalid boundary in multipart: %s' % 80 boundary) 81 82 83 # 84 # Content-Length should contain the length of the body we are about 85 # to receive. 86 # 87 try: 88 content_length = int(META.get('HTTP_CONTENT_LENGTH', 89 META.get('CONTENT_LENGTH',0))) 90 except (ValueError, TypeError): 91 # For now set it to 0...we'll try again later on down. 92 content_length = 0 93 94 if content_length <= 0: 95 # This means we shouldn't continue...raise an error. 96 raise MultiPartParserError("Invalid content length: %r" % content_length) 97 98 self._boundary = boundary 99 self._input_data = input_data 100 101 # For compatibility with low-level network APIs (with 32-bit integers), 102 # the chunk size should be < 2^31, but still divisible by 4. 103 self._chunk_size = min(2147483644, *[x.chunk_size for x in upload_handlers 104 if x.chunk_size]) 105 106 self._meta = META 107 self._encoding = encoding or settings.DEFAULT_CHARSET 108 self._content_length = content_length 109 self._upload_handlers = upload_handlers 110 111 112 def parse(self): 113 """ 114 Parse the POST data and break it into a FILES MultiValueDict 115 and a POST MultiValueDict. 116 117 *returns* -- A tuple containing the POST and FILES dictionary, 118 respectively. 119 """ 120 from django.core.files.uploadhandler import StopUpload, SkipFile 121 from django.http import QueryDict 122 123 encoding = self._encoding 124 handlers = self._upload_handlers 125 126 limited_input_data = LimitBytes(self._input_data, self._content_length) 127 128 # See if the handler will want to take care of the parsing. 129 # This allows overriding everything if somebody wants it. 130 for handler in handlers: 131 result = handler.handle_raw_input(limited_input_data, 132 self._meta, 133 self._content_length, 134 self._boundary, 135 encoding) 136 if result is not None: 137 return result[0], result[1] 138 139 # Create the data structures to be used later. 140 self._post = QueryDict('', mutable=True) 141 self._files = MultiValueDict() 142 143 # Instantiate the parser and stream: 144 stream = LazyStream(ChunkIter(limited_input_data, self._chunk_size)) 145 146 # Whether or not to signal a file-completion at the beginning of the loop. 147 old_field_name = None 148 counters = [0] * len(handlers) 149 150 try: 151 for item_type, meta_data, field_stream in Parser(stream, self._boundary): 152 if old_field_name: 153 # We run this at the beginning of the next loop 154 # since we cannot be sure a file is complete until 155 # we hit the next boundary/part of the multipart content. 156 self.handle_file_complete(old_field_name, counters) 157 158 try: 159 disposition = meta_data['content-disposition'][1] 160 field_name = disposition['name'].strip() 161 except (KeyError, IndexError, AttributeError): 162 continue 163 164 transfer_encoding = meta_data.get('content-transfer-encoding') 165 field_name = force_unicode(field_name, encoding, errors='replace') 166 167 if item_type is FIELD: 168 # This is a post field, we can just set it in the post 169 if transfer_encoding == 'base64': 170 raw_data = field_stream.read() 171 try: 172 data = str(raw_data).decode('base64') 173 except: 174 data = raw_data 175 else: 176 data = field_stream.read() 177 178 self._post.appendlist(field_name, 179 force_unicode(data, encoding, errors='replace')) 180 elif item_type is FILE: 181 # This is a file, use the handler... 182 file_successful = True 183 file_name = disposition.get('filename') 184 if not file_name: 185 continue 186 file_name = force_unicode(file_name, encoding, errors='replace') 187 file_name = self.IE_sanitize(unescape_entities(file_name)) 188 189 190 content_type = meta_data.get('content-type', ('',))[0].strip() 191 try: 192 charset = meta_data.get('content-type', (0,{}))[1].get('charset', None) 193 except: 194 charset = None 195 196 try: 197 content_length = int(meta_data.get('content-length')[0]) 198 except (IndexError, TypeError, ValueError): 199 content_length = None 200 201 counters = [0] * len(handlers) 202 try: 203 for handler in handlers: 204 retval = handler.new_file(field_name, file_name, 205 content_type, content_length, 206 charset) 207 if retval: 208 break 209 210 for chunk in field_stream: 211 if transfer_encoding == 'base64': 212 # We only special-case base64 transfer encoding 213 try: 214 chunk = str(chunk).decode('base64') 215 except Exception, e: 216 # Since this is only a chunk, any error is an unfixable error. 217 raise MultiValueParseError("Could not decode base64 data: %r" % e) 218 219 for i, handler in enumerate(handlers): 220 chunk_length = len(chunk) 221 chunk = handler.receive_data_chunk(chunk, 222 counters[i]) 223 counters[i] += chunk_length 224 if chunk is None: 225 # If the chunk received by the handler is None, then don't continue. 226 break 227 228 except SkipFile, e: 229 file_successful = False 230 # Just use up the rest of this file... 231 exhaust(field_stream) 232 else: 233 # Handle file upload completions on next iteration. 234 old_field_name = field_name 235 else: 236 # If this is neither a FIELD or a FILE, just exhaust the stream. 237 exhaust(stream) 238 except StopUpload, e: 239 if not e.connection_reset: 240 exhaust(limited_input_data) 241 else: 242 # Make sure that the request data is all fed 243 exhaust(limited_input_data) 244 245 # Signal that the upload has completed. 246 for handler in handlers: 247 retval = handler.upload_complete() 248 if retval: 249 break 250 251 return self._post, self._files 252 253 def handle_file_complete(self, old_field_name, counters): 254 """ 255 Handle all the signalling that takes place when a file is complete. 256 """ 257 for i, handler in enumerate(self._upload_handlers): 258 file_obj = handler.file_complete(counters[i]) 259 if file_obj: 260 # If it returns a file object, then set the files dict. 261 self._files.appendlist(force_unicode(old_field_name, 262 self._encoding, 263 errors='replace'), 264 file_obj) 265 break 266 267 def IE_sanitize(self, filename): 268 """cleanup filename from IE full paths""" 269 return filename and filename[filename.rfind("\\")+1:].strip() 270 271 272 class LazyStream(object): 273 """ 274 The LazyStream wrapper allows one to pull and "unget" bytes from a stream. 275 276 Given a producer object (an iterator that yields bytestrings), the 277 LazyStream object will support iteration, reading, and keeping a 278 "look-back" variable in case you need to "unget" some bytes. 279 """ 280 def __init__(self, producer, length=None): 281 """ 282 Every LazyStream must have a producer when instantiated. 283 284 A producer is an iterable that returns a string each time it 285 is called. 286 """ 287 self._producer = producer 288 self._empty = False 289 self._leftover = '' 290 self.length = length 291 self._position = 0 292 self._remaining = length 293 294 # These fields are to do sanity checking to make sure we don't 295 # have infinite loops getting/ungetting from the stream. The 296 # purpose overall is to raise an exception if we perform lots 297 # of stream get/unget gymnastics without getting 298 # anywhere. Naturally this is not sound, but most probably 299 # would indicate a bug if the exception is raised. 300 301 # largest position tell us how far this lazystream has ever 302 # been advanced 303 self._largest_position = 0 304 305 # "modifications since" will start at zero and increment every 306 # time the position is modified but a new largest position is 307 # not achieved. 308 self._modifications_since = 0 309 310 def tell(self): 311 return self.position 312 313 def read(self, size=None): 314 def parts(): 315 remaining = (size is not None and [size] or [self._remaining])[0] 316 # do the whole thing in one shot if no limit was provided. 317 if remaining is None: 318 yield ''.join(self) 319 return 320 321 # otherwise do some bookkeeping to return exactly enough 322 # of the stream and stashing any extra content we get from 323 # the producer 324 while remaining != 0: 325 assert remaining > 0, 'remaining bytes to read should never go negative' 326 327 chunk = self.next() 328 329 emitting = chunk[:remaining] 330 self.unget(chunk[remaining:]) 331 remaining -= len(emitting) 332 yield emitting 333 334 out = ''.join(parts()) 335 return out 336 337 def next(self): 338 """ 339 Used when the exact number of bytes to read is unimportant. 340 341 This procedure just returns whatever is chunk is conveniently 342 returned from the iterator instead. Useful to avoid 343 unnecessary bookkeeping if performance is an issue. 344 """ 345 if self._leftover: 346 output = self._leftover 347 self._leftover = '' 348 else: 349 output = self._producer.next() 350 self.position += len(output) 351 return output 352 353 def close(self): 354 """ 355 Used to invalidate/disable this lazy stream. 356 357 Replaces the producer with an empty list. Any leftover bytes 358 that have already been read will still be reported upon read() 359 and/or next(). 360 """ 361 self._producer = [] 362 363 def __iter__(self): 364 return self 365 366 def unget(self, bytes): 367 """ 368 Places bytes back onto the front of the lazy stream. 369 370 Future calls to read() will return those bytes first. The 371 stream position and thus tell() will be rewound. 372 """ 373 self.position -= len(bytes) 374 self._leftover = ''.join([bytes, self._leftover]) 375 376 def _set_position(self, value): 377 if value > self._largest_position: 378 self._modifications_since = 0 379 self._largest_position = value 380 else: 381 self._modifications_since += 1 382 if self._modifications_since > 500: 383 raise MultiPartParserError("LazyStream thinks it is somehow stuck. Report this to the Django developers\n" + repr(vars(self))) 384 385 self._position = value 386 387 position = property(lambda self: self._position, _set_position) 388 389 class ChunkIter(object): 390 """ 391 An iterable that will yield chunks of data. 392 Given a file-like object as the constructor, 393 this object will yield chunks of read operations 394 from that object. 395 """ 396 def __init__(self, flo, chunk_size=64 * 1024): 397 self.flo = flo 398 self.chunk_size = chunk_size 399 400 def next(self): 401 try: 402 data = self.flo.read(self.chunk_size) 403 except InputStreamExhausted: 404 raise StopIteration() 405 if data: 406 return data 407 else: 408 raise StopIteration() 409 410 def __iter__(self): 411 return self 412 413 414 class LimitBytes(object): 415 """ Limit bytes for a file object. """ 416 def __init__(self, fileobject, length): 417 self._file = fileobject 418 self.remaining = length 419 420 def read(self, num_bytes=None): 421 """ 422 Read data from the underlying file. 423 If you ask for too much or there isn't anything left, 424 this will raise an InputStreamExhausted error. 425 """ 426 if self.remaining <= 0: 427 raise InputStreamExhausted() 428 if num_bytes is None: 429 num_bytes = self.remaining 430 else: 431 num_bytes = min(num_bytes, self.remaining) 432 self.remaining -= num_bytes 433 return self._file.read(num_bytes) 434 435 class InterBoundaryIter(object): 436 """ 437 A Producer that will iterate over boundaries. 438 """ 439 def __init__(self, stream, boundary): 440 self._stream = stream 441 self._boundary = boundary 442 443 def __iter__(self): 444 return self 445 446 def next(self): 447 try: 448 return LazyStream(BoundaryIter(self._stream, self._boundary)) 449 except InputStreamExhausted: 450 raise StopIteration() 451 452 453 class BoundaryIter(object): 454 """ 455 A Producer that is sensitive to boundaries. 456 457 Will happily yield bytes until a boundary is found. Will yield the 458 bytes before the boundary, throw away the boundary bytes 459 themselves, and push the post-boundary bytes back on the stream. 460 461 The future calls to .next() after locating the boundary will raise 462 a StopIteration exception. 463 """ 464 465 def __init__(self, stream, boundary): 466 self._stream = stream 467 self._boundary = boundary 468 self._done = False 469 # rollback an additional six bytes because the format is like 470 # this: CRLF<boundary>[--CRLF] 471 self._rollback = len(boundary) + 6 472 473 # Try to use mx fast string search if available. Otherwise 474 # use Python find. Wrap the latter for consistency. 475 unused_char = self._stream.read(1) 476 if not unused_char: 477 raise InputStreamExhausted() 478 self._stream.unget(unused_char) 479 try: 480 from mx.TextTools import FS 481 self._fs = FS(boundary).find 482 except ImportError: 483 self._fs = lambda data: data.find(boundary) 484 485 def __iter__(self): 486 return self 487 488 def next(self): 489 if self._done: 490 raise StopIteration() 491 492 stream = self._stream 493 rollback = self._rollback 494 495 bytes_read = 0 496 chunks = [] 497 for bytes in stream: 498 bytes_read += len(bytes) 499 chunks.append(bytes) 500 if bytes_read > rollback: 501 break 502 if not bytes: 503 break 504 else: 505 self._done = True 506 507 if not chunks: 508 raise StopIteration() 509 510 chunk = ''.join(chunks) 511 boundary = self._find_boundary(chunk, len(chunk) < self._rollback) 512 513 if boundary: 514 end, next = boundary 515 stream.unget(chunk[next:]) 516 self._done = True 517 return chunk[:end] 518 else: 519 # make sure we dont treat a partial boundary (and 520 # its separators) as data 521 if not chunk[:-rollback]:# and len(chunk) >= (len(self._boundary) + 6): 522 # There's nothing left, we should just return and mark as done. 523 self._done = True 524 return chunk 525 else: 526 stream.unget(chunk[-rollback:]) 527 return chunk[:-rollback] 528 529 def _find_boundary(self, data, eof = False): 530 """ 531 Finds a multipart boundary in data. 532 533 Should no boundry exist in the data None is returned 534 instead. Otherwise a tuple containing 535 the indices of the following are returned: 536 537 * the end of current encapsulation 538 539 * the start of the next encapsulation 540 """ 541 index = self._fs(data) 542 if index < 0: 543 return None 544 else: 545 end = index 546 next = index + len(self._boundary) 547 data_len = len(data) - 1 548 # backup over CRLF 549 if data[max(0,end-1)] == '\n': 550 end -= 1 551 if data[max(0,end-1)] == '\r': 552 end -= 1 553 # skip over --CRLF 554 #if data[min(data_len,next)] == '-': 555 # next += 1 556 #if data[min(data_len,next)] == '-': 557 # next += 1 558 #if data[min(data_len,next)] == '\r': 559 # next += 1 560 #if data[min(data_len,next)] == '\n': 561 # next += 1 562 return end, next 563 564 def exhaust(stream_or_iterable): 565 """ 566 Completely exhausts an iterator or stream. 567 568 Raise a MultiPartParserError if the argument is not a stream or an 569 iterable. 570 """ 571 iterator = None 572 try: 573 iterator = iter(stream_or_iterable) 574 except TypeError: 575 iterator = ChunkIter(stream_or_iterable, 16384) 576 577 if iterator is None: 578 raise MultiPartParserError('multipartparser.exhaust() was passed a non-iterable or stream parameter') 579 580 for __ in iterator: 581 pass 582 583 def ParseBoundaryStream(stream, max_header_size): 584 """ 585 Parses one and exactly one stream that encapsulates a boundary. 586 """ 587 # Stream at beginning of header, look for end of header 588 # and parse it if found. The header must fit within one 589 # chunk. 590 chunk = stream.read(max_header_size) 591 # 'find' returns the top of these four bytes, so we'll 592 # need to munch them later to prevent them from polluting 593 # the payload. 594 header_end = chunk.find('\r\n\r\n') 595 596 def _parse_header(line): 597 main_value_pair, params = parse_header(line) 598 try: 599 name, value = main_value_pair.split(':', 1) 600 except: 601 raise ValueError("Invalid header: %r" % line) 602 return name, (value, params) 603 604 if header_end == -1: 605 # we find no header, so we just mark this fact and pass on 606 # the stream verbatim 607 stream.unget(chunk) 608 return (RAW, {}, stream) 609 610 header = chunk[:header_end] 611 612 # here we place any excess chunk back onto the stream, as 613 # well as throwing away the CRLFCRLF bytes from above. 614 stream.unget(chunk[header_end + 4:]) 615 616 TYPE = RAW 617 outdict = {} 618 619 # Eliminate blank lines 620 for line in header.split('\r\n'): 621 # This terminology ("main value" and "dictionary of 622 # parameters") is from the Python docs. 623 try: 624 name, (value, params) = _parse_header(line) 625 except: 626 continue 627 628 if name == 'content-disposition': 629 TYPE = FIELD 630 if params.get('filename'): 631 TYPE = FILE 632 633 outdict[name] = value, params 634 635 if TYPE == RAW: 636 stream.unget(chunk) 637 638 return (TYPE, outdict, stream) 639 640 641 class Parser(object): 642 def __init__(self, stream, boundary): 643 self._stream = stream 644 self._separator = '--' + boundary 645 646 def __iter__(self): 647 648 boundarystream = InterBoundaryIter(self._stream, 649 self._separator) 650 651 for sub_stream in boundarystream: 652 # Iterate over each part 653 yield ParseBoundaryStream(sub_stream, 1024) 654 655 def parse_header(line): 656 """ Parse the header into a key-value. """ 657 plist = _parse_header_params(';' + line) 658 key = plist.pop(0).lower() 659 pdict = {} 660 for p in plist: 661 i = p.find('=') 662 if i >= 0: 663 name = p[:i].strip().lower() 664 value = p[i+1:].strip() 665 if len(value) >= 2 and value[0] == value[-1] == '"': 666 value = value[1:-1] 667 value = value.replace('\\\\', '\\').replace('\\"', '"') 668 pdict[name] = value 669 return key, pdict 670 671 def _parse_header_params(s): 672 plist = [] 673 while s[:1] == ';': 674 s = s[1:] 675 end = s.find(';') 676 while end > 0 and s.count('"', 0, end) % 2: 677 end = s.find(';', end + 1) 678 if end < 0: 679 end = len(s) 680 f = s[:end] 681 plist.append(f.strip()) 682 s = s[end:] 683 return plist -
django/http/__init__.py
9 9 except ImportError: 10 10 from cgi import parse_qsl 11 11 12 from django.utils.datastructures import MultiValueDict, FileDict12 from django.utils.datastructures import MultiValueDict, ImmutableList 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="!*'();:@&=+$,/?%#[]" … … 25 25 26 26 # The encoding used in GET/POST dicts. None means use default setting. 27 27 _encoding = None 28 _upload_handlers = () 28 29 29 30 def __init__(self): 30 31 self.GET, self.POST, self.COOKIES, self.META, self.FILES = {}, {}, {}, {}, {} … … 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 _initialize_handlers(self): 107 from django.conf import settings 108 from django.core.files.uploadhandler import load_handler 109 handlers = [] 110 # We go through each handler in the settings variable 111 # and instantiate the handler by calling HandlerClass(request). 112 for handler in settings.FILE_UPLOAD_HANDLERS: 113 handlers.append(load_handler(handler, self)) 114 self._upload_handlers = handlers 137 115 116 def _set_upload_handlers(self, upload_handlers): 117 """ 118 Set the upload handler to the new handler given in the parameter. 119 """ 120 if hasattr(self, '_files'): 121 raise AttributeError("You cannot set the upload handlers after the upload has been processed.") 122 self._upload_handlers = upload_handlers 138 123 124 def _get_upload_handlers(self): 125 if not self._upload_handlers: 126 # If thre are no upload handlers defined, initialize them from settings. 127 self._initialize_handlers() 128 return self._upload_handlers 129 130 upload_handlers = property(_get_upload_handlers, _set_upload_handlers) 131 132 def parse_file_upload(self, META, post_data): 133 """Returns a tuple of (POST QueryDict, FILES MultiValueDict).""" 134 self.upload_handlers = ImmutableList(self.upload_handlers, 135 warning="You cannot alter the upload handlers after the upload has been processed.") 136 parser = MultiPartParser(META, post_data, self.upload_handlers, 137 self.encoding) 138 return parser.parse() 139 140 139 141 class QueryDict(MultiValueDict): 140 142 """ 141 143 A specialized MultiValueDict that takes a query string when initialized. -
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): 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 684 688 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:689 file_size = new_data.file_size 690 except AttributeError: 691 file_size = len(new_data['content']) 692 if not file_size: 689 693 raise validators.CriticalValidationError, ugettext("The submitted file is empty.") 690 694 691 695 def render(self, data): 692 696 return mark_safe(u'<input type="file" id="%s" class="v%s" name="%s" />' % \ 693 697 (self.get_id(), self.__class__.__name__, self.field_name)) 694 698 699 def prepare(self, new_data): 700 if hasattr(new_data, 'upload_errors'): 701 upload_errors = new_data.upload_errors() 702 new_data[self.field_name] = { '_file_upload_error': upload_errors } 703 695 704 def html2python(data): 696 705 if data is None: 697 706 raise EmptyValue -
django/db/models/base.py
19 19 from django.utils.datastructures import SortedDict 20 20 from django.utils.functional import curry 21 21 from django.utils.encoding import smart_str, force_unicode, smart_unicode 22 from django.core.files.move import file_move_safe 22 23 from django.conf import settings 23 24 24 25 try: … … 471 472 def _get_FIELD_size(self, field): 472 473 return os.path.getsize(self._get_FIELD_filename(field)) 473 474 474 def _save_FIELD_file(self, field, filename, raw_ contents, save=True):475 def _save_FIELD_file(self, field, filename, raw_field, save=True): 475 476 directory = field.get_directory_name() 476 477 try: # Create the date-based directory if it doesn't exist. 477 478 os.makedirs(os.path.join(settings.MEDIA_ROOT, directory)) 478 479 except OSError: # Directory probably already exists. 479 480 pass 481 482 # Put the deprecation warning first since there are multiple 483 # locations where we use the new and old interface. 484 if isinstance(raw_field, dict): 485 import warnings 486 from django.core.files.uploadedfile import SimpleUploadedFile 487 raw_field = SimpleUploadedFile.from_dict(raw_field) 488 warnings.warn("The dictionary usage for files is deprecated. Use the new object interface instead.", DeprecationWarning) 489 elif isinstance(raw_field, basestring): 490 import warnings 491 from django.core.files.uploadedfile import SimpleUploadedFile 492 raw_field = SimpleUploadedFile(filename, raw_field) 493 warnings.warn("The string interface for save_FIELD_file is deprecated.", DeprecationWarning) 494 495 if filename is None: 496 filename = raw_field.file_name 497 480 498 filename = field.get_filename(filename) 481 499 482 500 # If the filename already exists, keep adding an underscore to the name of … … 493 511 setattr(self, field.attname, filename) 494 512 495 513 full_filename = self._get_FIELD_filename(field) 496 fp = open(full_filename, 'wb') 497 fp.write(raw_contents) 498 fp.close() 514 if hasattr(raw_field, 'temporary_file_path'): 515 # This file has a file path that we can move. 516 raw_field.close() 517 file_move_safe(raw_field.temporary_file_path(), full_filename) 518 else: 519 from django.core.files import locks 520 fp = open(full_filename, 'wb') 521 # exclusive lock 522 locks.lock(fp, locks.LOCK_EX) 523 # This is a normal uploadedfile that we can stream. 524 for chunk in raw_field.chunk(65535): 525 fp.write(chunk) 526 locks.unlock(fp) 527 fp.close() 499 528 529 500 530 # Save the width and/or height, if applicable. 501 531 if isinstance(field, ImageField) and (field.width_field or field.height_field): 502 532 from django.utils.images import get_image_dimensions -
django/db/models/fields/__init__.py
802 802 setattr(cls, 'get_%s_filename' % self.name, curry(cls._get_FIELD_filename, field=self)) 803 803 setattr(cls, 'get_%s_url' % self.name, curry(cls._get_FIELD_url, field=self)) 804 804 setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self)) 805 setattr(cls, 'save_%s_file' % self.name, lambda instance, filename, raw_ contents, save=True: instance._save_FIELD_file(self, filename, raw_contents, save))805 setattr(cls, 'save_%s_file' % self.name, lambda instance, filename, raw_field, save=True: instance._save_FIELD_file(self, filename, raw_field, save)) 806 806 dispatcher.connect(self.delete_file, signal=signals.post_delete, sender=cls) 807 807 808 808 def delete_file(self, instance): … … 825 825 if new_data.get(upload_field_name, False): 826 826 func = getattr(new_object, 'save_%s_file' % self.name) 827 827 if rel: 828 f unc(new_data[upload_field_name][0]["filename"], new_data[upload_field_name][0]["content"], save)828 file = new_data[upload_field_name][0] 829 829 else: 830 f unc(new_data[upload_field_name]["filename"], new_data[upload_field_name]["content"], save)830 file = new_data[upload_field_name] 831 831 832 try: 833 file_name = file.file_name 834 except AttributeError: 835 file_name = file['filename'] 836 func(file_name, file, save) 837 832 838 def get_directory_name(self): 833 839 return os.path.normpath(force_unicode(datetime.datetime.now().strftime(smart_str(self.upload_to)))) 834 840 … … 840 846 def save_form_data(self, instance, data): 841 847 from django.newforms.fields import UploadedFile 842 848 if data and isinstance(data, UploadedFile): 843 getattr(instance, "save_%s_file" % self.name)(data.filename, data. content, save=False)849 getattr(instance, "save_%s_file" % self.name)(data.filename, data.data, save=False) 844 850 845 851 def formfield(self, **kwargs): 846 852 defaults = {'form_class': forms.FileField} -
django/conf/global_settings.py
224 224 # Example: "http://media.lawrence.com" 225 225 MEDIA_URL = '' 226 226 227 # A tuple that enumerates the upload handlers 228 # in order. 229 FILE_UPLOAD_HANDLERS = ( 230 'django.core.files.uploadhandler.MemoryFileUploadHandler', 231 'django.core.files.uploadhandler.TemporaryFileUploadHandler', 232 ) 233 234 # Number of bytes the length of the request can be before it is 235 # streamed to the file system instead of parsed entirely in memory. 236 FILE_UPLOAD_MAX_MEMORY_SIZE = 2621440 237 238 # Directory to upload streamed files temporarily. 239 # A value of `None` means that it will use the default temporary 240 # directory for the server's operating system. 241 FILE_UPLOAD_TEMP_DIR = None 242 227 243 # Default formatting for date objects. See all available format strings here: 228 244 # http://www.djangoproject.com/documentation/templates/#now 229 245 DATE_FORMAT = 'N j, Y' -
django/core/handlers/wsgi.py
112 112 # Populates self._post and self._files 113 113 if self.method == 'POST': 114 114 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) 115 self._raw_post_data = '' 116 self._post, self._files = self.parse_file_upload(self.META, self.environ['wsgi.input']) 118 117 else: 119 118 self._post, self._files = http.QueryDict(self.raw_post_data, encoding=self._encoding), datastructures.MultiValueDict() 120 119 else: -
django/core/handlers/modpython.py
53 53 def _load_post_and_files(self): 54 54 "Populates self._post and self._files" 55 55 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) 56 self._raw_post_data = '' 57 self._post, self._files = self.parse_file_upload(self.META, self._req) 57 58 else: 58 59 self._post, self._files = http.QueryDict(self.raw_post_data, encoding=self._encoding), datastructures.MultiValueDict() 59 60 -
django/core/files/locks.py
1 """ 2 Locking portability based partially on example by 3 Jonathan Feignberg <jdf@pobox.com> in python cookbook: 4 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65203 5 Licensed under the Python Software License. 6 7 Example Usage:: 8 9 from django.core.files import locks 10 11 f = open('./file', 'wb') 12 13 locks.lock(f, locks.LOCK_EX) 14 f.write('Django') 15 f.close() 16 """ 17 18 __all__ = ('LOCK_EX','LOCK_SH','LOCK_NB','lock','unlock') 19 20 system_type = None 21 22 try: 23 import win32con 24 import win32file 25 import pywintypes 26 LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK 27 LOCK_SH = 0 28 LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY 29 __overlapped = pywintypes.OVERLAPPED() 30 system_type = 'nt' 31 except (ImportError, AttributeError): 32 pass 33 34 try: 35 import fcntl 36 LOCK_EX = fcntl.LOCK_EX 37 LOCK_SH = fcntl.LOCK_SH 38 LOCK_NB = fcntl.LOCK_NB 39 system_type = 'posix' 40 except (ImportError, AttributeError): 41 pass 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 elif system_type == 'posix': 52 def lock(file, flags): 53 fcntl.flock(file.fileno(), flags) 54 55 def unlock(file): 56 fcntl.flock(file.fileno(), fcntl.LOCK_UN) 57 else: 58 # File locking is not supported. 59 LOCK_EX = LOCK_SH = LOCK_NB = None 60 61 # Dummy functions that don't do anything. 62 def lock(file, flags): 63 pass 64 65 def unlock(file): 66 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 chunk(self, chunk_size=None): 25 """ 26 Read the file to generate chunks of chunk_size bytes. 27 """ 28 if not chunk_size: 29 chunk_size = UploadedFile.DEFAULT_CHUNK_SIZE 30 31 if hasattr(self, 'seek'): 32 self.seek(0) 33 # Assume the pointer is at zero... 34 counter = self.file_size 35 36 while counter > 0: 37 yield self.read(chunk_size) 38 counter -= chunk_size 39 40 41 def multiple_chunks(self, chunk_size=None): 42 """ 43 Return True if you can expect multiple chunks, False otherwise. 44 Note: If a particular file representation is in memory, then 45 override this to return False. 46 """ 47 if not chunk_size: 48 chunk_size = UploadedFile.DEFAULT_CHUNK_SIZE 49 return self.file_size < chunk_size 50 51 52 def read(self, num_bytes=None): 53 """ 54 Read from the file in whatever representation it has. 55 """ 56 raise NotImplementedError() 57 58 def open(self): 59 """ 60 Open the file, if one needs to. 61 """ 62 pass 63 64 65 def close(self): 66 """ 67 Close the file, if one needs to. 68 """ 69 pass 70 71 def __getitem__(self, key): 72 """ 73 This maintains backwards compatibility. 74 """ 75 import warnings 76 warnings.warn("The dictionary access of uploaded file objects is deprecated. Use the new object interface instead.", DeprecationWarning) 77 # Dictionary to translate labels 78 # for backwards compatbility. 79 # Should be removed at some point. 80 backwards_translate = { 81 'filename': 'file_name', 82 'content-type': 'content_type', 83 } 84 85 if key == 'content': 86 return self.read() 87 else: 88 return getattr(self, backwards_translate.get(key, key)) 89 90 def __repr__(self): 91 """ 92 This representation could be anything and can be overridden. 93 This is mostly done to make it look somewhat useful. 94 """ 95 _dict = { 96 'file_name': self.file_name, 97 'content_type': self.content_type, 98 'content': '<omitted>', 99 } 100 return repr(_dict) 101 102 103 class TemporaryUploadedFile(UploadedFile): 104 """ 105 Upload a file to a temporary file. 106 """ 107 108 def __init__(self, file, file_name, content_type, file_size, charset): 109 self.file = file 110 self.file_name = file_name 111 self.path = file.name 112 self.content_type = content_type 113 self.file_size = file_size 114 self.charset = charset 115 self.file.seek(0) 116 117 def temporary_file_path(self): 118 """ 119 Return the full path of this file. 120 """ 121 return self.path 122 123 def read(self, *args, **kwargs): 124 return self.file.read(*args, **kwargs) 125 126 def open(self): 127 """ 128 Assume the person meant to seek. 129 """ 130 self.seek(0) 131 132 def seek(self, *args, **kwargs): 133 self.file.seek(*args, **kwargs) 134 135 136 class InMemoryUploadedFile(UploadedFile): 137 """ 138 Upload a file into memory. 139 """ 140 def __init__(self, file, field_name, file_name, content_type, charset): 141 self.file = file 142 self.field_name = field_name 143 self.file_name = file_name 144 self.content_type = content_type 145 self.charset = charset 146 self.file.seek(0) 147 148 def seek(self, *args, **kwargs): 149 self.file.seek(*args, **kwargs) 150 151 def open(self): 152 self.seek(0) 153 154 def read(self, *args, **kwargs): 155 return self.file.read(*args, **kwargs) 156 157 def chunk(self, chunk_size=None): 158 """ 159 Return the entirety of the data regardless. 160 """ 161 self.file.seek(0) 162 return self.read() 163 164 def multiple_chunks(self, chunk_size=None): 165 """ 166 Since it's in memory, we'll never have multiple chunks. 167 """ 168 return False 169 170 171 class SimpleUploadedFile(InMemoryUploadedFile): 172 """ 173 A simple representation of a file, which 174 just has content, size, and a name. 175 """ 176 def __init__(self, name, content, content_type='text/plain'): 177 try: 178 from cStringIO import StringIO 179 except ImportError: 180 from StringIO import StringIO 181 self.file = StringIO(content or '') 182 self.file_name = name 183 self.field_name = None 184 self.file_size = len(content or '') 185 self.content_type = content_type 186 self.charset = None 187 self.file.seek(0) 188 189 def from_dict(cls, file_dict): 190 """ 191 Creates a SimpleUploadedFile object from 192 a dictionary object with the following keys: 193 - filename 194 - content-type 195 - content 196 """ 197 return cls(file_dict['filename'], 198 file_dict['content'], 199 file_dict.get('content-type', 'text/plain')) 200 201 from_dict = classmethod(from_dict) 202 -
django/core/files/__init__.py
1 -
django/core/files/uploadhandler.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 from django.core.exceptions import ImproperlyConfigured 12 13 from django.core.files.uploadedfile import TemporaryUploadedFile, InMemoryUploadedFile 14 15 __all__ = ('UploadFileException','StopUpload', 'SkipFile', 16 'FileUploadHandler', 'TemporaryFileUploadHandler', 17 'MemoryFileUploadHandler', 'load_handler') 18 19 20 class UploadFileException(Exception): 21 """ Any error having to do with Uploading Files. """ 22 pass 23 24 class StopUpload(UploadFileException): 25 """ This exception is raised when an upload must abort. """ 26 def __init__(self, connection_reset=False): 27 """ 28 Initializes a StopUpload Exception object. 29 30 By specifying connection_reset=True, Django knows 31 it is okay to halt the upload without feeding 32 in the rest of the upload to nowhere. 33 (And thus keeping the browser from the 34 Connection Reset error.) 35 """ 36 self.connection_reset = connection_reset 37 38 def __unicode__(self): 39 if self.connection_reset: 40 return u'StopUpload: Halt current upload.' 41 else: 42 return u'StopUpload: Feed in request data.' 43 44 class SkipFile(UploadFileException): 45 """ This exception is raised when a file needs to be skipped. """ 46 pass 47 48 class FileUploadHandler(object): 49 """ FileUploadHandler will take data and handle file uploads 50 in a streamed fashion. 51 """ 52 chunk_size = 64 * 2 ** 10 #: The default chunk size is 64 KB. 53 54 def __init__(self, request=None): 55 " Initialize some local variables. " 56 self.file_name = None 57 self.content_type = None 58 self.content_length = None 59 self.charset = None 60 self.request = request 61 62 def handle_raw_input(self, input_data, META, content_length, boundary, encoding=None): 63 """ 64 Handle the raw input from the client. 65 Parameters: 66 *input_data* -- An object that supports reading via .read(). 67 *content_length* -- The (integer) value of the Content-Length header from the client. 68 *boundary* -- The boundary from the Content-Type header. Be sure to prepend two '--'. 69 """ 70 pass 71 72 def new_file(self, field_name, file_name, content_type, content_length, charset=None): 73 """ 74 Signal that a new file has been started. 75 76 Warning: Do not trust content_length, if you get it at all. 77 """ 78 self.field_name = field_name 79 self.file_name = file_name 80 self.content_type = content_type 81 self.content_length = content_length 82 self.charset = charset 83 84 def receive_data_chunk(self, raw_data, start): 85 """ 86 Receive data from the streamed upload parser. 87 Start is the position in the file of the chunk. 88 """ 89 raise NotImplementedError() 90 91 def file_complete(self, file_size): 92 """ 93 Signal that a file has completed. 94 File size corresponds to the actual size accumulated 95 by all the chunks. 96 97 This should return a valid UploadedFile object. 98 """ 99 raise NotImplementedError() 100 101 def upload_complete(self): 102 """ 103 Signal that the upload is complete. 104 Do any cleanup that is necessary for this handler. 105 """ 106 pass 107 108 109 110 class TemporaryFileUploadHandler(FileUploadHandler): 111 """ 112 Upload the streaming data into a temporary file. 113 """ 114 def __init__(self, *args, **kwargs): 115 """ Import settings for later. """ 116 super(TemporaryFileUploadHandler, self).__init__(*args, **kwargs) 117 global settings 118 from django.conf import settings 119 120 def new_file(self, file_name, *args, **kwargs): 121 """ 122 Create the file object to append to as data is coming in. 123 """ 124 super(TemporaryFileUploadHandler, self).new_file(file_name, *args, **kwargs) 125 self.file = TemporaryFile(settings.FILE_UPLOAD_TEMP_DIR) 126 self.write = self.file.write 127 128 def receive_data_chunk(self, raw_data, start): 129 """ 130 Once we get the data, we will save it to our file. 131 """ 132 self.write(raw_data) 133 134 def file_complete(self, file_size): 135 """ 136 Signal that a file has completed. 137 File size corresponds to the actual size accumulated 138 by all the chunks. 139 140 This should return a valid UploadedFile object. 141 """ 142 self.file.seek(0) 143 return TemporaryUploadedFile(self.file, self.file_name, 144 self.content_type, file_size, 145 self.charset) 146 147 148 class MemoryFileUploadHandler(FileUploadHandler): 149 """ 150 The MemoryFileUploadHandler will place the data directly into memory. 151 """ 152 153 def handle_raw_input(self, input_data, META, content_length, boundary, encoding=None): 154 """ 155 Use the content_length to signal whether or not this handler should be in use. 156 """ 157 global settings 158 from django.conf import settings 159 160 # If the the post is too large, we cannot use the Memory handler. 161 if content_length > settings.FILE_UPLOAD_MAX_MEMORY_SIZE: 162 self.activated = False 163 else: 164 self.activated = True 165 166 def new_file(self, *args, **kwargs): 167 super(MemoryFileUploadHandler, self).new_file(*args, **kwargs) 168 if self.activated: 169 self.file = StringIO() 170 return "Stop" 171 172 def receive_data_chunk(self, raw_data, start): 173 """ 174 Add the data to the StringIO file. 175 """ 176 if self.activated: 177 self.file.write(raw_data) 178 else: 179 return raw_data 180 181 def file_complete(self, file_size): 182 """ 183 Return a file object if we're activated. 184 """ 185 if not self.activated: 186 return 187 188 return InMemoryUploadedFile(self.file, self.field_name, self.file_name, 189 self.content_type, self.charset) 190 191 192 class TemporaryFile(object): 193 """ 194 A temporary file that tries to delete itself when garbage collected. 195 """ 196 def __init__(self, dir): 197 import tempfile 198 if not dir: 199 dir = tempfile.gettempdir() 200 try: 201 (fd, name) = tempfile.mkstemp(suffix='.upload', dir=dir) 202 self.file = os.fdopen(fd, 'w+b') 203 except (OSError, IOError): 204 raise OSError("Could not create temporary file for uploading, have you set settings.FILE_UPLOAD_TEMP_DIR correctly?") 205 self.name = name 206 207 def __getattr__(self, name): 208 a = getattr(self.__dict__['file'], name) 209 if type(a) != type(0): 210 setattr(self, name, a) 211 return a 212 213 def __del__(self): 214 try: 215 os.unlink(self.name) 216 except OSError: 217 pass 218 219 def load_handler(path, *args, **kwargs): 220 """ 221 Given a path to a handler return the instantiation of that handler. 222 E.g.:: 223 load_handler('django.core.files.uploadhandler.TemporaryFileUploadHandler', request) 224 will return a TemporaryFileUploadHandler object. 225 """ 226 i = path.rfind('.') 227 module, attr = path[:i], path[i+1:] 228 try: 229 mod = __import__(module, {}, {}, [attr]) 230 except ImportError, e: 231 raise ImproperlyConfigured('Error importing upload handler module %s: "%s"' % (module, e)) 232 except ValueError, e: 233 raise ImproperlyConfigured('Error importing upload handler module Is FILE_UPLOAD_HANDLERS a correctly defined list or tuple?') 234 try: 235 cls = getattr(mod, attr) 236 except AttributeError: 237 raise ImproperlyConfigured('Module "%s" does not define a "%s" upload handler backend' % (module, attr)) 238 return cls(*args, **kwargs) -
django/core/files/move.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 locks 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 locks.lock(new_file, locks.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
4 4 5 5 import copy 6 6 import datetime 7 import warnings 7 8 import os 8 9 import re 9 10 import time … … 416 417 417 418 class UploadedFile(StrAndUnicode): 418 419 "A wrapper for files uploaded in a FileField" 419 def __init__(self, filename, content):420 def __init__(self, filename, data): 420 421 self.filename = filename 421 self. content = content422 self.data = data 422 423 423 424 def __unicode__(self): 424 425 """ … … 444 445 return None 445 446 elif not data and initial: 446 447 return initial 448 449 if isinstance(data, dict): 450 # We warn once, then support both ways below. 451 warnings.warn("The dictionary usage for files is deprecated. Use the new object interface instead.", DeprecationWarning) 452 447 453 try: 448 f = UploadedFile(data['filename'], data['content']) 449 except TypeError: 454 file_name = data.file_name 455 file_size = data.file_size 456 except AttributeError: 457 try: 458 file_name = data.get('filename') 459 file_size = bool(data['content']) 460 except (AttributeError, KeyError): 461 raise ValidationError(self.error_messages['invalid']) 462 463 if not file_name: 450 464 raise ValidationError(self.error_messages['invalid']) 451 except KeyError: 452 raise ValidationError(self.error_messages['missing']) 453 if not f.content: 465 if not file_size: 454 466 raise ValidationError(self.error_messages['empty']) 455 return f456 467 468 return UploadedFile(file_name, data) 469 457 470 class ImageField(FileField): 458 471 default_error_messages = { 459 472 'invalid_image': _(u"Upload a valid image. The file you uploaded was either not an image or a corrupted image."), … … 470 483 elif not data and initial: 471 484 return initial 472 485 from PIL import Image 473 from cStringIO import StringIO 486 487 # We need to get the file, it either has a path 488 # or we have to read it all into memory... 489 if hasattr(data, 'temporary_file_path'): 490 file = data.temporary_file_path() 491 else: 492 try: 493 from cStringIO import StringIO 494 except ImportError: 495 from StringIO import StringIO 496 if hasattr(data, 'read'): 497 file = StringIO(data.read()) 498 else: 499 file = StringIO(data['content']) 500 474 501 try: 475 502 # load() is the only method that can spot a truncated JPEG, 476 503 # but it cannot be called sanely after verify() 477 trial_image = Image.open( StringIO(f.content))504 trial_image = Image.open(file) 478 505 trial_image.load() 506 507 # Since we're about to use the file again, we have to 508 # reset the cursor of the file object if it has a cursor 509 # to reset. 510 if hasattr(file, 'reset'): 511 file.reset() 512 479 513 # verify() is the only method that can spot a corrupt PNG, 480 514 # but it must be called immediately after the constructor 481 trial_image = Image.open( StringIO(f.content))515 trial_image = Image.open(file) 482 516 trial_image.verify() 483 517 except Exception: # Python Imaging Library doesn't recognize it as an image 484 518 raise ValidationError(self.error_messages['invalid_image']) -
django/utils/datastructures.py
332 332 except TypeError: # Special-case if current isn't a dict. 333 333 current = {bits[-1]: v} 334 334 335 class FileDict(dict):335 class ImmutableList(tuple): 336 336 """ 337 A dictionary used to hold uploaded file contents. The only special feature 338 here is that repr() of this object won't dump the entire contents of the 339 file to the output. A handy safeguard for a large file upload. 337 A tuple-like object that raises useful 338 errors when it is asked to mutate. 339 Example:: 340 a = ImmutableList(range(5), warning=AttributeError("You cannot mutate this.")) 341 a[3] = '4' 342 (Raises the AttributeError) 340 343 """ 341 def __repr__(self): 342 if 'content' in self: 343 d = dict(self, content='<omitted>') 344 return dict.__repr__(d) 345 return dict.__repr__(self) 344 345 def __new__(cls, *args, **kwargs): 346 if 'warning' in kwargs: 347 warning = kwargs['warning'] 348 del kwargs['warning'] 349 else: 350 warning = 'ImmutableList object is immutable.' 351 self = tuple.__new__(cls, *args, **kwargs) 352 self.warning = warning 353 return self 354 355 def complain(self, *wargs, **kwargs): 356 if isinstance(self.warning, Exception): 357 raise self.warning 358 else: 359 raise AttributeError, self.warning 360 361 # All list mutation functions become complain. 362 __delitem__ = __delslice__ = __iadd__ = __imul__ = complain 363 __setitem__ = __setslice__ = complain 364 append = extend = insert = pop = remove = complain 365 sort = reverse = complain -
django/utils/text.py
3 3 from django.utils.encoding import force_unicode 4 4 from django.utils.functional import allow_lazy 5 5 from django.utils.translation import ugettext_lazy 6 from htmlentitydefs import name2codepoint 6 7 7 8 # Capitalizes the first letter of a string. 8 9 capfirst = lambda x: x and force_unicode(x)[0].upper() + force_unicode(x)[1:] … … 218 219 yield bit 219 220 smart_split = allow_lazy(smart_split, unicode) 220 221 222 def _replace_entity(match): 223 text = match.group(1) 224 if text[0] == u'#': 225 text = text[1:] 226 try: 227 if text[0] in u'xX': 228 c = int(text[1:], 16) 229 else: 230 c = int(text) 231 return unichr(c) 232 except ValueError: 233 return match.group(0) 234 else: 235 try: 236 return unichr(name2codepoint[text]) 237 except (ValueError, KeyError): 238 return match.group(0) 239 240 _entity_re = re.compile(r"&(#?[xX]?(?:[0-9a-fA-F]+|\w{1,8}));") 241 242 def unescape_entities(text): 243 return _entity_re.sub(_replace_entity, text) 244 unescape_entities = allow_lazy(unescape_entities, unicode) -
tests/modeltests/model_forms/models.py
67 67 68 68 class ImageFile(models.Model): 69 69 description = models.CharField(max_length=20) 70 image = models.FileField(upload_to=tempfile.gettempdir()) 70 try: 71 # If PIL is available, try testing PIL. 72 # Otherwise, it's equivalent to TextFile above. 73 import Image 74 image = models.ImageField(upload_to=tempfile.gettempdir()) 75 except ImportError: 76 image = models.FileField(upload_to=tempfile.gettempdir()) 71 77 72 78 def __unicode__(self): 73 79 return self.description … … 75 81 __test__ = {'API_TESTS': """ 76 82 >>> from django import newforms as forms 77 83 >>> from django.newforms.models import ModelForm 84 >>> from django.core.files.uploadedfile import SimpleUploadedFile 85 >>> from warnings import filterwarnings 86 >>> filterwarnings("ignore") 78 87 79 88 The bare bones, absolutely nothing custom, basic case. 80 89 … … 792 801 793 802 # Upload a file and ensure it all works as expected. 794 803 804 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test1.txt', 'hello world')}) 805 >>> f.is_valid() 806 True 807 >>> type(f.cleaned_data['file']) 808 <class 'django.newforms.fields.UploadedFile'> 809 >>> instance = f.save() 810 >>> instance.file 811 u'...test1.txt' 812 813 >>> os.unlink(instance.get_file_filename()) 814 795 815 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': {'filename': 'test1.txt', 'content': 'hello world'}}) 796 816 >>> f.is_valid() 797 817 True … … 814 834 u'...test1.txt' 815 835 816 836 # Delete the current file since this is not done by Django. 817 818 837 >>> os.unlink(instance.get_file_filename()) 819 838 820 839 # Override the file by uploading a new one. 821 840 822 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': {'filename': 'test2.txt', 'content': 'hello world'}}, instance=instance)841 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test2.txt', 'hello world')}, instance=instance) 823 842 >>> f.is_valid() 824 843 True 825 844 >>> instance = f.save() 826 845 >>> instance.file 827 846 u'...test2.txt' 828 847 848 # Delete the current file since this is not done by Django. 849 >>> os.unlink(instance.get_file_filename()) 850 851 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': {'filename': 'test2.txt', 'content': 'hello world'}}) 852 >>> f.is_valid() 853 True 854 >>> instance = f.save() 855 >>> instance.file 856 u'...test2.txt' 857 858 # Delete the current file since this is not done by Django. 859 >>> os.unlink(instance.get_file_filename()) 860 829 861 >>> instance.delete() 830 862 831 863 # Test the non-required FileField … … 838 870 >>> instance.file 839 871 '' 840 872 841 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': {'filename': 'test3.txt', 'content': 'hello world'}}, instance=instance)873 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test3.txt', 'hello world')}, instance=instance) 842 874 >>> f.is_valid() 843 875 True 844 876 >>> instance = f.save() 845 877 >>> instance.file 846 878 u'...test3.txt' 879 880 # Delete the current file since this is not done by Django. 881 >>> os.unlink(instance.get_file_filename()) 847 882 >>> instance.delete() 848 883 884 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': {'filename': 'test3.txt', 'content': 'hello world'}}) 885 >>> f.is_valid() 886 True 887 >>> instance = f.save() 888 >>> instance.file 889 u'...test3.txt' 890 891 # Delete the current file since this is not done by Django. 892 >>> os.unlink(instance.get_file_filename()) 893 >>> instance.delete() 894 849 895 # ImageField ################################################################### 850 896 851 897 # ImageField and FileField are nearly identical, but they differ slighty when … … 858 904 859 905 >>> image_data = open(os.path.join(os.path.dirname(__file__), "test.png")).read() 860 906 907 >>> f = ImageFileForm(data={'description': u'An image'}, files={'image': SimpleUploadedFile('test.png', image_data)}) 908 >>> f.is_valid() 909 True 910 >>> type(f.cleaned_data['image']) 911 <class 'django.newforms.fields.UploadedFile'> 912 >>> instance = f.save() 913 >>> instance.image 914 u'...test.png' 915 916 # Delete the current file since this is not done by Django. 917 >>> os.unlink(instance.get_image_filename()) 918 861 919 >>> f = ImageFileForm(data={'description': u'An image'}, files={'image': {'filename': 'test.png', 'content': image_data}}) 862 920 >>> f.is_valid() 863 921 True … … 885 943 886 944 # Override the file by uploading a new one. 887 945 888 >>> f = ImageFileForm(data={'description': u'Changed it'}, files={'image': {'filename': 'test2.png', 'content': image_data}}, instance=instance)946 >>> f = ImageFileForm(data={'description': u'Changed it'}, files={'image': SimpleUploadedFile('test2.png', image_data)}, instance=instance) 889 947 >>> f.is_valid() 890 948 True 891 949 >>> instance = f.save() 892 950 >>> instance.image 893 951 u'...test2.png' 894 952 953 # Delete the current file since this is not done by Django. 954 >>> os.unlink(instance.get_image_filename()) 895 955 >>> instance.delete() 896 956 957 >>> f = ImageFileForm(data={'description': u'Changed it'}, files={'image': {'filename': 'test2.png', 'content': image_data}}) 958 >>> f.is_valid() 959 True 960 >>> instance = f.save() 961 >>> instance.image 962 u'...test2.png' 963 964 # Delete the current file since this is not done by Django. 965 >>> os.unlink(instance.get_image_filename()) 966 >>> instance.delete() 967 897 968 # Test the non-required ImageField 898 969 899 970 >>> f = ImageFileForm(data={'description': u'Test'}) … … 904 975 >>> instance.image 905 976 '' 906 977 907 >>> f = ImageFileForm(data={'description': u'And a final one'}, files={'image': {'filename': 'test3.png', 'content': image_data}}, instance=instance)978 >>> f = ImageFileForm(data={'description': u'And a final one'}, files={'image': SimpleUploadedFile('test3.png', image_data)}, instance=instance) 908 979 >>> f.is_valid() 909 980 True 910 981 >>> instance = f.save() 911 982 >>> instance.image 912 983 u'...test3.png' 984 985 # Delete the current file since this is not done by Django. 986 >>> os.unlink(instance.get_image_filename()) 913 987 >>> instance.delete() 914 988 989 >>> f = ImageFileForm(data={'description': u'And a final one'}, files={'image': {'filename': 'test3.png', 'content': image_data}}) 990 >>> f.is_valid() 991 True 992 >>> instance = f.save() 993 >>> instance.image 994 u'...test3.png' 995 >>> instance.delete() 996 915 997 """} -
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 } 29 25 qd["image_file"] = SimpleUploadedFile('test.jpg', img, 'image/jpeg') 26 30 27 manip = Photo.AddManipulator() 31 28 manip.do_html2python(qd) 32 29 p = manip.save(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/tests.py
26 26 from regressions import tests as regression_tests 27 27 from util import tests as util_tests 28 28 from widgets import tests as widgets_tests 29 from warnings import filterwarnings 30 filterwarnings("ignore") 29 31 30 32 __test__ = { 31 33 'extra_tests': extra_tests, -
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.get('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 30 # Check to see if unicode names worked out. 31 if not request.FILES['file_unicode'].file_name.endswith(u'test_\u4e2d\u6587_Orl\xe9ans.jpg'): 32 return HttpResponseServerError() 33 34 for key, value in form_data.items(): 35 if key.endswith('_hash'): 36 continue 37 if key + '_hash' not in form_data: 38 continue 39 submitted_hash = form_data[key + '_hash'] 40 if isinstance(value, UploadedFile): 41 new_hash = sha.new(value.read()).hexdigest() 42 else: 43 new_hash = sha.new(value).hexdigest() 44 if new_hash != submitted_hash: 45 return HttpResponseServerError() 46 47 return HttpResponse('') 48 20 49 def get_view(request): 21 50 "A simple login protected view" 22 51 return HttpResponse("Hello world") … … 37 66 def login_protected_redirect_view(request): 38 67 "A view that redirects all requests to the GET view" 39 68 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 69 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 # This file contains chinese symbols for a name. 262 name3 = os.path.join(dir, u'test_中文_Orl\u00e9ans.jpg') 263 file3 = open(name3, 'w+b') 264 file3.write('b' * (2 ** 10)) 265 file3.seek(0) 266 267 post_data = { 268 'name': 'Ringo', 269 'file_field1': file1, 270 'file_field2': file2, 271 'file_unicode': file3, 272 } 273 274 for key in post_data.keys(): 275 try: 276 post_data[key + '_hash'] = sha.new(post_data[key].read()).hexdigest() 277 post_data[key].seek(0) 278 except AttributeError: 279 post_data[key + '_hash'] = sha.new(post_data[key]).hexdigest() 280 281 response = self.client.post('/test_client_regress/file_upload_verify/', post_data) 282 283 for name in (name1, name2, name3): 284 try: 285 os.unlink(name) 286 except: 287 pass 288 289 self.assertEqual(response.status_code, 200) 290 291 246 292 class LoginTests(TestCase): 247 293 fixtures = ['testdata'] 248 294 -
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) -
tests/regressiontests/datastructures/tests.py
117 117 >>> d['person']['2']['firstname'] 118 118 ['Adrian'] 119 119 120 ### FileDict ################################################################ 121 122 >>> d = FileDict({'content': 'once upon a time...'}) 120 ### ImmutableList ################################################################ 121 >>> d = ImmutableList(range(10)) 122 >>> d.sort() 123 Traceback (most recent call last): 124 File "<stdin>", line 1, in <module> 125 File "/var/lib/python-support/python2.5/django/utils/datastructures.py", line 359, in complain 126 raise AttributeError, self.warning 127 AttributeError: ImmutableList object is immutable. 123 128 >>> repr(d) 124 "{'content': '<omitted>'}" 125 >>> d = FileDict({'other-key': 'once upon a time...'}) 126 >>> repr(d) 127 "{'other-key': 'once upon a time...'}" 129 '(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)' 130 >>> d = ImmutableList(range(10), warning="Object is immutable!") 131 >>> d[1] 132 1 133 >>> d[1] = 'test' 134 Traceback (most recent call last): 135 File "<stdin>", line 1, in <module> 136 File "/var/lib/python-support/python2.5/django/utils/datastructures.py", line 359, in complain 137 raise AttributeError, self.warning 138 AttributeError: Object is immutable! 128 139 """ -
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> … … 136 136 Marc Fargas <telenieko@telenieko.com> 137 137 Szilveszter Farkas <szilveszter.farkas@gmail.com> 138 138 favo@exoweb.net 139 fdr <drfarina@gmail.com> 139 140 Dmitri Fedortchenko <zeraien@gmail.com> 140 141 Liang Feng <hutuworm@gmail.com> 141 142 Bill Fenner <fenner@gmail.com> -
docs/upload_handling.txt
1 ============ 2 File Uploads 3 ============ 4 5 **New in Django development version** 6 7 Most Web sites wouldn't be complete without a way to upload files. Before the 8 file even gets into ``request.FILES`` Django has to decide where to put the 9 all the incoming data. This document describes how Django takes the incoming 10 request and populates ``request.FILES``. 11 12 .. _Request and response objects: ../request_response/#attributes 13 14 Default Behavior 15 ================ 16 17 After setting up a quick model that contains a `FileField`_ and enabling the 18 `Admin interface`_ you can quickly use a form to upload files to your site. 19 By default, if the upload is smaller than **2.5 Megabytes** in size, Django 20 will hold the entire contents of the upload in memory, and the file will 21 quickly be saved to its final destination (as defined by 22 `settings.MEDIA_ROOT`_) without any intermediary. 23 24 If the entire upload is larger than 2.5 Megabytes, then it will -- by 25 default -- write the contents of the uploaded file to a temporary file 26 in your operating system's default temporary directory. On a posix platform, 27 this means that you could expect Django to generate a file similar in name 28 to ``/tmp/tmpzfp6I6.upload``. During the upload, you may notice this file 29 grow in size as Django loads the data onto disk. 30 31 .. note:: 32 You may find that the default temporary directory is not writable. 33 This is especially true if you are on shared hosting. If this is 34 the case, Django will raise an error when you try to upload. Please 35 read below in `Extending the Default`_ to change this directory. 36 37 38 .. _FileField: ../model-api/#filefield 39 .. _Admin interface: ../tutorial02/#activate-the-admin-site 40 .. _settings.MEDIA_ROOT: ../settings/#media-root 41 42 Extending the Default 43 ===================== 44 45 Suppose you have quite a bit of memory. You may decide that you want Django 46 to stream only if the entire upload exceeds 10 Megabytes. You may even 47 decide that whatever directory is the platform default is not suitable 48 for your project. If this is the case, Django provides these two settings: 49 50 =============================== ====================================== 51 Setting Description 52 =============================== ====================================== 53 ``FILE_UPLOAD_MAX_MEMORY_SIZE`` The maximum size of a request in bytes 54 for which Django will try to load the 55 entire upload contents in memory. 56 57 ``FILE_UPLOAD_TEMP_DIR`` The directory on the file system where 58 uploaded contents will be temporarily 59 stored if not completely in memory. 60 E.g.: ``"/tmp"`` 61 =============================== ====================================== 62 63 There is one final setting -- ``FILE_UPLOAD_HANDLERS`` -- which allows complete 64 customization of the upload process. 65 66 Upload Handlers 67 =============== 68 69 Through upload handlers Django provides the flexibility to extend the 70 upload process beyond simple storage. You can use custom handlers to enforce 71 user-level quotas, compress data on the fly, render progress bars, and 72 even send data to another warehouse directly without storing it locally. 73 74 There are two pieces to the Django upload handling: the upload handler and the 75 uploaded file. These are both represented by python classes -- 76 ``FileUploadHandler`` and ``UploadedFile`` respectively. Throughout the lifetime 77 of the upload process, Django will call on the upload handler to handle the 78 upload, while the upload handler is expected to provide Django an ``UploadedFile`` 79 object to successfully complete a file's upload. 80 81 Setting Default Upload Handlers for your Project 82 ------------------------------------------------ 83 84 Similar to `Middleware`_, upload handlers have an order that is initially 85 defined in ``settings.FILE_UPLOAD_HANDLERS``. The default value is:: 86 87 ("django.core.files.fileuploadhandler.MemoryFileUploadHandler", 88 "django.core.files.fileuploadhandler.TemporaryFileUploadHandler",) 89 90 This literally means: Try putting the upload in memory first and failing 91 that put the upload in a temporary file. 92 93 This behavior, however, is completely dependent on how each of those handlers 94 is written. For example, if someone gave you a ``Rot13UploadHandler``, you can 95 update your ``settings`` to contain:: 96 97 FILE_UPLOAD_HANDLERS = ( 98 "app.uploadhandlers.rot13.Rot13UploadHandler", 99 "django.core.files.fileuploadhandler.MemoryFileUploadHandler", 100 "django.core.files.fileuploadhandler.TemporaryFileUploadHandler", 101 ) 102 103 And the ``Rot13UploadHandler`` will perform the ``rot13`` operation on all 104 data before the data get to subsequent handlers. 105 106 .. _Middleware: ../middleware/ 107 108 Modifying your Upload Handlers Dynamically 109 ------------------------------------------ 110 111 During the lifetime of your project, you may realize that a particular 112 view or views require different uploading behavior. For this reason, 113 the ``request`` object contains a list of handlers 114 (``request.upload_handlers``) that will be called in order. To append 115 a handler to the list, you would append to it like any other list. 116 For example, suppose you had an ``ProgressBarUploadHandler`` class. 117 To append it to your upload handlers you would write:: 118 119 request.upload_handlers.append(ProgressBarUploadHandler()) 120 121 However, since the progress bar handler would probably need to run before the 122 other handlers get a chance, you'd probably want to insert it. That is, you'd 123 write:: 124 125 request.upload_handlers.insert(0, ProgressBarUploadHandler()) 126 127 If you want to replace the upload handlers completely, you can just assign a new 128 list:: 129 130 request.upload_handlers = [ProgressBarUploadHandler()] 131 132 And all Django will do is keep a progress, but do nothing with the file itself! 133 One more note: After the upload has completed, you are no longer allowed to 134 assign or modify this list, and Django will raise an error if you try to modify 135 this list after an upload. 136 137 Writing a File Upload Handler 138 ----------------------------- 139 140 All file upload handlers are subclasses of ``FileUploadHandler``, found in 141 ``django.core.files.fileuploadhandler``. To create your handler, you need to 142 define the required methods followed by the methods that make most sense to you: 143 144 chunk_size 145 ~~~~~~~~~~ 146 147 An integer attribute that specifies what sized chunks Django should store 148 into memory and feed into the handler. The chunk sizes should be divisible by 149 ``4`` and should not exceed ``2 ** 31 - 1`` in size. When there are multiple 150 chunk sizes provided by multiple handlers, Django will use the smallest chunk 151 size. 152 153 __init__ 154 ~~~~~~~~ 155 156 The constructor, which should optionally support taking a request object as its 157 first argument. When called via ``settings.FILE_UPLOAD_HANDLERS``, it will be 158 passed a request object. 159 160 Interface: ``__init__(self, request=None)`` 161 162 ``request`` is the request object. Do not access ``request.POST`` or 163 ``request.FILES``. 164 165 new_file 166 ~~~~~~~~ 167 168 ``new_file`` signals that a new file is starting. You can initialize a file or 169 whatever else is needed on a new file upload. 170 171 Interface: ``new_file(self, field_name, file_name, content_type, content_length, 172 charset)`` 173 174 ``field_name`` is a string name of the field this was POSTed as. 175 176 ``file_name`` is the unicode filename that was provided by the browser. 177 178 ``content_type`` is the MIME type provided by the browser -- E.g. 179 ``'image/jpeg'``. 180 181 ``content_length`` is the length of the image given by the browser if provided, 182 ``None`` otherwise. 183 184 ``charset`` is the charset given by the browser if provided, ``None`` otherwise. 185 186 Returns: ``None`` if you want other handlers to get ``new_file`` called. 187 Something nonzero if you don't want subsequent handlers to get a chance. 188 189 receive_data_chunk 190 ~~~~~~~~~~~~~~~~~~ 191 *required* 192 193 Receives a segment of data from the file upload. For example: The 194 ``TemporaryFileUploadHandler`` takes the data and writes it to disk. 195 196 Interface: ``receive_data_chunk(self, raw_data, start)`` 197 198 ``raw_data`` is a byte string containing the uploaded data. 199 200 ``start`` is the position in the file where this ``raw_data`` chunk begins. 201 202 Returns: ``None`` if you don't want the subsequent upload handlers to receive 203 the data. Whatever else you return gets fed into the subsequent upload handlers' 204 ``receive_data_chunk`` method. In this way, one handler can be a "filter" for 205 other handlers. 206 207 Exceptions: If you raise a ``StopUpload`` or a ``SkipFile`` exception, the 208 upload will abort or the file will be skipped respectively. 209 210 file_complete 211 ~~~~~~~~~~~~~ 212 *required* 213 214 Signals that a file has finished uploading. Expected to return an 215 ``UploadedFile`` object if the file has been packaged successfully. 216 217 Interface: ``file_complete(self, file_size)`` 218 219 ``file_size`` is the number of bytes you have received for this file. 220 221 Returns: An ``UploadedFile`` object to set in the ``request.FILES`` dictionary. 222 ``None`` if you want the subsequent upload handlers to get an ``UploadedFile`` 223 object. 224 225 upload_complete 226 ~~~~~~~~~~~~~~~ 227 228 Defines when the entire upload has completed. 229 230 Interface: ``upload_complete(self)`` 231 232 handle_raw_input 233 ~~~~~~~~~~~~~~~~ 234 235 Allows the handler to completely override the parsing of the raw HTTP-layered 236 input. 237 238 Interface: ``handle_raw_input(self, input_data, META, content_length, boundary, 239 encoding)`` 240 241 ``input_data`` is a file-like object that supports the read operation. 242 243 ``META`` is the same object as ``request.META``. 244 245 ``content_length`` is the length of the data in ``input_data``. Don't read more 246 than ``content_length`` bytes from ``input_data``. 247 248 ``boundary`` is the MIME boundary for this request. 249 250 ``encoding`` is the encoding of the request. 251 252 Returns: ``None`` if you want to go on to the next stage, ``(POST, FILES)`` if 253 you want to return the new data structures suitable for the request directly. 254 255 Defining an Uploaded File 256 ------------------------- 257 258 All file upload handlers are subclasses of ``UploadedFile``, found in 259 ``django.core.files.uploadedfile``. The uploaded file object is returned by the 260 handler above in ``file_complete``. To create your own uploaded file class, you 261 need to define the required methods followed by the methods that make most sense 262 to you: 263 264 read 265 ~~~~ 266 *required* 267 268 Interface: ``read(self, num_bytes=None)`` 269 270 Returns: A byte string of length ``num_bytes`` (or the size of the file if 271 ``num_bytes`` is not supplied). 272 273 chunk 274 ~~~~~ 275 276 A generator to yield small chunks from the file. With the ``read()`` defined, 277 the ``UploadedFile`` class already defines a ``chunk()`` method that's probably 278 suitable. 279 280 Interface: ``chunk(self, chunk_size=None)`` 281 282 multiple_chunks 283 ~~~~~~~~~~~~~~~ 284 285 Interface: ``multiple_chunks(self, chunk_size=None)`` 286 287 Returns: ``True`` or ``False`` depending on whether or not the user of this 288 file can expect more than one chunk when calling ``chunk(self, chunk_size)``. 289 If all the data is in memory, you should return ``False``. 290 291 temporary_file_path 292 ~~~~~~~~~~~~~~~~~~~ 293 294 If defined, this method should return the file path of the file on the 295 operating system. This will let users move files rather than write to 296 new files if the option is available. 297 298 Interface: ``temporary_file_path(self)`` 299 300 Returns: A file path in a file system on the local computer. 301 302 upload_errors 303 ~~~~~~~~~~~~~ 304 305 If defined, this method should return a string describing errors that 306 occured during uploads. 307 308 Interface: ``upload_errors(self)`` 309 310 Returns: A ``unicode`` string describing an error that occured with the 311 file's upload. -
docs/request_response.txt
80 80 strings. 81 81 82 82 ``FILES`` 83 **New in Django development version** 83 84 A dictionary-like object containing all uploaded files. Each key in 84 85 ``FILES`` is the ``name`` from the ``<input type="file" name="" />``. Each 85 value in ``FILES`` is a standard Python dictionary with the following three86 keys:86 value in ``FILES`` is an ``UploadedFile`` object containing at least the 87 following attributes: 87 88 88 * ``filename`` -- The name of the uploaded file, as a Python string. 89 * ``content-type`` -- The content type of the uploaded file. 90 * ``content`` -- The raw content of the uploaded file. 89 * ``read(num_bytes=None)`` -- Read a number of bytes from the file. 90 * ``file_name`` -- The name of the uploaded file. 91 * ``file_size`` -- The size, in bytes, of the uploaded file. 92 * ``chunk()`` -- A generator that yields sequential chunks of data. 91 93 92 Note that ``FILES`` will only contain data if the request method was POST93 and the ``<form>`` that posted to the request had94 ``enctype="multipart/form-data"``. Otherwise, ``FILES`` will be a blank95 dictionary-like object.94 See `File Uploads`_ for more information. Note that ``FILES`` will only 95 contain data if the request method was POST and the ``<form>`` that posted 96 to the request had ``enctype="multipart/form-data"``. Otherwise, ``FILES`` 97 will be a blank dictionary-like object. 96 98 99 .. _File Uploads: ../upload_handling/ 100 97 101 ``META`` 98 102 A standard Python dictionary containing all available HTTP headers. 99 103 Available headers depend on the client and server, but here are some -
docs/settings.txt
279 279 280 280 The database backend to use. The build-in database backends are 281 281 ``'postgresql_psycopg2'``, ``'postgresql'``, ``'mysql'``, ``'mysql_old'``, 282 ``'sqlite3'`` and ``'oracle'``.282 ``'sqlite3'``, ``'oracle'``, and ``'oracle'``. 283 283 284 284 In the Django development version, you can use a database backend that doesn't 285 285 ship with Django by setting ``DATABASE_ENGINE`` to a fully-qualified path (i.e. … … 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_HANDLERS 517 -------------------- 518 519 **New in Django development version** 520 521 Default:: 522 523 ("django.core.files.fileuploadhandler.MemoryFileUploadHandler", 524 "django.core.files.fileuploadhandler.TemporaryFileUploadHandler",) 525 526 A tuple of handlers to use for uploading. 527 528 FILE_UPLOAD_MAX_MEMORY_SIZE 529 --------------------------- 530 531 **New in Django development version** 532 533 Default: ``2621440`` 534 535 The maximum size (in bytes) that an upload will be before it gets streamed to the file system. 536 537 FILE_UPLOAD_TEMP_DIR 538 -------------------- 539 540 **New in Django development version** 541 542 Default: ``None`` 543 544 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. 545 516 546 FIXTURE_DIRS 517 547 ------------- 518 548 -
docs/newforms.txt
805 805 need to bind the file data containing the mugshot image:: 806 806 807 807 # Bound form with an image field 808 >>> from django.core.files.uploadedfile import SimpleUploadedFile 808 809 >>> data = {'subject': 'hello', 809 810 ... 'message': 'Hi there', 810 811 ... 'sender': 'foo@example.com', 811 812 ... 'cc_myself': True} 812 >>> file_data = {'mugshot': {'filename':'face.jpg' 813 ... 'content': <file data>}} 813 >>> file_data = {'mugshot': SimpleUploadedFile('face.jpg', <file data>)} 814 814 >>> f = ContactFormWithMugshot(data, file_data) 815 815 816 816 In practice, you will usually specify ``request.FILES`` as the source