app/django/core/files/uploadhandler.py
changeset 323 ff1a9aa48cfd
equal deleted inserted replaced
322:6641e941ef1e 323:ff1a9aa48cfd
       
     1 """
       
     2 Base file upload handler classes, and the built-in concrete subclasses
       
     3 """
       
     4 
       
     5 try:
       
     6     from cStringIO import StringIO
       
     7 except ImportError:
       
     8     from StringIO import StringIO
       
     9 
       
    10 from django.conf import settings
       
    11 from django.core.exceptions import ImproperlyConfigured
       
    12 from django.core.files.uploadedfile import TemporaryUploadedFile, InMemoryUploadedFile
       
    13 
       
    14 __all__ = ['UploadFileException','StopUpload', 'SkipFile', 'FileUploadHandler',
       
    15            'TemporaryFileUploadHandler', 'MemoryFileUploadHandler',
       
    16            'load_handler']
       
    17 
       
    18 class UploadFileException(Exception):
       
    19     """
       
    20     Any error having to do with uploading files.
       
    21     """
       
    22     pass
       
    23 
       
    24 class StopUpload(UploadFileException):
       
    25     """
       
    26     This exception is raised when an upload must abort.
       
    27     """
       
    28     def __init__(self, connection_reset=False):
       
    29         """
       
    30         If ``connection_reset`` is ``True``, Django knows will halt the upload
       
    31         without consuming the rest of the upload. This will cause the browser to
       
    32         show a "connection reset" error.
       
    33         """
       
    34         self.connection_reset = connection_reset
       
    35 
       
    36     def __unicode__(self):
       
    37         if self.connection_reset:
       
    38             return u'StopUpload: Halt current upload.'
       
    39         else:
       
    40             return u'StopUpload: Consume request data, then halt.'
       
    41 
       
    42 class SkipFile(UploadFileException):
       
    43     """
       
    44     This exception is raised by an upload handler that wants to skip a given file.
       
    45     """
       
    46     pass
       
    47     
       
    48 class StopFutureHandlers(UploadFileException):
       
    49     """
       
    50     Upload handers that have handled a file and do not want future handlers to
       
    51     run should raise this exception instead of returning None.
       
    52     """
       
    53     pass
       
    54 
       
    55 class FileUploadHandler(object):
       
    56     """
       
    57     Base class for streaming upload handlers.
       
    58     """
       
    59     chunk_size = 64 * 2 ** 10 #: The default chunk size is 64 KB.
       
    60 
       
    61     def __init__(self, request=None):
       
    62         self.file_name = None
       
    63         self.content_type = None
       
    64         self.content_length = None
       
    65         self.charset = None
       
    66         self.request = request
       
    67 
       
    68     def handle_raw_input(self, input_data, META, content_length, boundary, encoding=None):
       
    69         """
       
    70         Handle the raw input from the client.
       
    71 
       
    72         Parameters:
       
    73 
       
    74             :input_data:
       
    75                 An object that supports reading via .read().
       
    76             :META:
       
    77                 ``request.META``.
       
    78             :content_length:
       
    79                 The (integer) value of the Content-Length header from the
       
    80                 client.
       
    81             :boundary: The boundary from the Content-Type header. Be sure to
       
    82                 prepend two '--'.
       
    83         """
       
    84         pass
       
    85 
       
    86     def new_file(self, field_name, file_name, content_type, content_length, charset=None):
       
    87         """
       
    88         Signal that a new file has been started.
       
    89 
       
    90         Warning: As with any data from the client, you should not trust
       
    91         content_length (and sometimes won't even get it).
       
    92         """
       
    93         self.field_name = field_name
       
    94         self.file_name = file_name
       
    95         self.content_type = content_type
       
    96         self.content_length = content_length
       
    97         self.charset = charset
       
    98 
       
    99     def receive_data_chunk(self, raw_data, start):
       
   100         """
       
   101         Receive data from the streamed upload parser. ``start`` is the position
       
   102         in the file of the chunk.
       
   103         """
       
   104         raise NotImplementedError()
       
   105 
       
   106     def file_complete(self, file_size):
       
   107         """
       
   108         Signal that a file has completed. File size corresponds to the actual
       
   109         size accumulated by all the chunks.
       
   110 
       
   111         Subclasses must should return a valid ``UploadedFile`` object.
       
   112         """
       
   113         raise NotImplementedError()
       
   114 
       
   115     def upload_complete(self):
       
   116         """
       
   117         Signal that the upload is complete. Subclasses should perform cleanup
       
   118         that is necessary for this handler.
       
   119         """
       
   120         pass
       
   121 
       
   122 class TemporaryFileUploadHandler(FileUploadHandler):
       
   123     """
       
   124     Upload handler that streams data into a temporary file.
       
   125     """
       
   126     def __init__(self, *args, **kwargs):
       
   127         super(TemporaryFileUploadHandler, self).__init__(*args, **kwargs)
       
   128 
       
   129     def new_file(self, file_name, *args, **kwargs):
       
   130         """
       
   131         Create the file object to append to as data is coming in.
       
   132         """
       
   133         super(TemporaryFileUploadHandler, self).new_file(file_name, *args, **kwargs)
       
   134         self.file = TemporaryUploadedFile(self.file_name, self.content_type, 0, self.charset)
       
   135 
       
   136     def receive_data_chunk(self, raw_data, start):
       
   137         self.file.write(raw_data)
       
   138 
       
   139     def file_complete(self, file_size):
       
   140         self.file.seek(0)
       
   141         self.file.size = file_size
       
   142         return self.file
       
   143 
       
   144 class MemoryFileUploadHandler(FileUploadHandler):
       
   145     """
       
   146     File upload handler to stream uploads into memory (used for small files).
       
   147     """
       
   148 
       
   149     def handle_raw_input(self, input_data, META, content_length, boundary, encoding=None):
       
   150         """
       
   151         Use the content_length to signal whether or not this handler should be in use.
       
   152         """
       
   153         # Check the content-length header to see if we should
       
   154         # If the the post is too large, we cannot use the Memory handler.
       
   155         if content_length > settings.FILE_UPLOAD_MAX_MEMORY_SIZE:
       
   156             self.activated = False
       
   157         else:
       
   158             self.activated = True
       
   159 
       
   160     def new_file(self, *args, **kwargs):
       
   161         super(MemoryFileUploadHandler, self).new_file(*args, **kwargs)
       
   162         if self.activated:
       
   163             self.file = StringIO()
       
   164             raise StopFutureHandlers()
       
   165 
       
   166     def receive_data_chunk(self, raw_data, start):
       
   167         """
       
   168         Add the data to the StringIO file.
       
   169         """
       
   170         if self.activated:
       
   171             self.file.write(raw_data)
       
   172         else:
       
   173             return raw_data
       
   174 
       
   175     def file_complete(self, file_size):
       
   176         """
       
   177         Return a file object if we're activated.
       
   178         """
       
   179         if not self.activated:
       
   180             return
       
   181 
       
   182         return InMemoryUploadedFile(
       
   183             file = self.file,
       
   184             field_name = self.field_name,
       
   185             name = self.file_name,
       
   186             content_type = self.content_type,
       
   187             size = file_size,
       
   188             charset = self.charset
       
   189         )
       
   190 
       
   191 
       
   192 def load_handler(path, *args, **kwargs):
       
   193     """
       
   194     Given a path to a handler, return an instance of that handler.
       
   195 
       
   196     E.g.::
       
   197         >>> load_handler('django.core.files.uploadhandler.TemporaryFileUploadHandler', request)
       
   198         <TemporaryFileUploadHandler object at 0x...>
       
   199 
       
   200     """
       
   201     i = path.rfind('.')
       
   202     module, attr = path[:i], path[i+1:]
       
   203     try:
       
   204         mod = __import__(module, {}, {}, [attr])
       
   205     except ImportError, e:
       
   206         raise ImproperlyConfigured('Error importing upload handler module %s: "%s"' % (module, e))
       
   207     except ValueError, e:
       
   208         raise ImproperlyConfigured('Error importing upload handler module. Is FILE_UPLOAD_HANDLERS a correctly defined list or tuple?')
       
   209     try:
       
   210         cls = getattr(mod, attr)
       
   211     except AttributeError:
       
   212         raise ImproperlyConfigured('Module "%s" does not define a "%s" upload handler backend' % (module, attr))
       
   213     return cls(*args, **kwargs)