Django

Code

Show
Ignore:
Timestamp:
07/01/08 10:10:51 (2 months ago)
Author:
jacob
Message:

Fixed #2070: refactored Django's file upload capabilities.

A description of the new features can be found in the new upload handling documentation; the executive summary is that Django will now happily handle uploads of large files without issues.

This changes the representation of uploaded files from dictionaries to bona fide objects; see BackwardsIncompatibleChanges for details.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/http/__init__.py

    r7334 r7814  
    1010    from cgi import parse_qsl 
    1111 
    12 from django.utils.datastructures import MultiValueDict, FileDic
     12from django.utils.datastructures import MultiValueDict, ImmutableLis
    1313from django.utils.encoding import smart_str, iri_to_uri, force_unicode 
    14  
     14from django.http.multipartparser import MultiPartParser 
     15from django.conf import settings 
     16from django.core.files import uploadhandler 
    1517from utils import * 
    1618 
    1719RESERVED_CHARS="!*'();:@&=+$,/?%#[]" 
    18  
    1920 
    2021class Http404(Exception): 
     
    2627    # The encoding used in GET/POST dicts. None means use default setting. 
    2728    _encoding = None 
     29    _upload_handlers = [] 
    2830 
    2931    def __init__(self): 
     
    103105    encoding = property(_get_encoding, _set_encoding) 
    104106 
    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 
    137  
     107    def _initialize_handlers(self): 
     108        self._upload_handlers = [uploadhandler.load_handler(handler, self) 
     109                                 for handler in settings.FILE_UPLOAD_HANDLERS] 
     110 
     111    def _set_upload_handlers(self, upload_handlers): 
     112        if hasattr(self, '_files'): 
     113            raise AttributeError("You cannot set the upload handlers after the upload has been processed.") 
     114        self._upload_handlers = upload_handlers 
     115 
     116    def _get_upload_handlers(self): 
     117        if not self._upload_handlers: 
     118            # If thre are no upload handlers defined, initialize them from settings. 
     119            self._initialize_handlers() 
     120        return self._upload_handlers 
     121 
     122    upload_handlers = property(_get_upload_handlers, _set_upload_handlers) 
     123 
     124    def parse_file_upload(self, META, post_data): 
     125        """Returns a tuple of (POST QueryDict, FILES MultiValueDict).""" 
     126        self.upload_handlers = ImmutableList( 
     127            self.upload_handlers, 
     128            warning = "You cannot alter upload handlers after the upload has been processed." 
     129        ) 
     130        parser = MultiPartParser(META, post_data, self.upload_handlers, self.encoding) 
     131        return parser.parse() 
    138132 
    139133class QueryDict(MultiValueDict):