uploadedfile.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. """
  2. Classes representing uploaded files.
  3. """
  4. import errno
  5. import os
  6. from io import BytesIO
  7. from django.conf import settings
  8. from django.core.files.base import File
  9. from django.core.files import temp as tempfile
  10. from django.utils.encoding import force_str
  11. __all__ = ('UploadedFile', 'TemporaryUploadedFile', 'InMemoryUploadedFile',
  12. 'SimpleUploadedFile')
  13. class UploadedFile(File):
  14. """
  15. A abstract uploaded file (``TemporaryUploadedFile`` and
  16. ``InMemoryUploadedFile`` are the built-in concrete subclasses).
  17. An ``UploadedFile`` object behaves somewhat like a file object and
  18. represents some file data that the user submitted with a form.
  19. """
  20. DEFAULT_CHUNK_SIZE = 64 * 2 ** 10
  21. def __init__(self, file=None, name=None, content_type=None, size=None, charset=None, content_type_extra=None):
  22. super(UploadedFile, self).__init__(file, name)
  23. self.size = size
  24. self.content_type = content_type
  25. self.charset = charset
  26. self.content_type_extra = content_type_extra
  27. def __repr__(self):
  28. return force_str("<%s: %s (%s)>" % (
  29. self.__class__.__name__, self.name, self.content_type))
  30. def _get_name(self):
  31. return self._name
  32. def _set_name(self, name):
  33. # Sanitize the file name so that it can't be dangerous.
  34. if name is not None:
  35. # Just use the basename of the file -- anything else is dangerous.
  36. name = os.path.basename(name)
  37. # File names longer than 255 characters can cause problems on older OSes.
  38. if len(name) > 255:
  39. name, ext = os.path.splitext(name)
  40. ext = ext[:255]
  41. name = name[:255 - len(ext)] + ext
  42. self._name = name
  43. name = property(_get_name, _set_name)
  44. class TemporaryUploadedFile(UploadedFile):
  45. """
  46. A file uploaded to a temporary location (i.e. stream-to-disk).
  47. """
  48. def __init__(self, name, content_type, size, charset, content_type_extra=None):
  49. if settings.FILE_UPLOAD_TEMP_DIR:
  50. file = tempfile.NamedTemporaryFile(suffix='.upload',
  51. dir=settings.FILE_UPLOAD_TEMP_DIR)
  52. else:
  53. file = tempfile.NamedTemporaryFile(suffix='.upload')
  54. super(TemporaryUploadedFile, self).__init__(file, name, content_type, size, charset, content_type_extra)
  55. def temporary_file_path(self):
  56. """
  57. Returns the full path of this file.
  58. """
  59. return self.file.name
  60. def close(self):
  61. try:
  62. return self.file.close()
  63. except OSError as e:
  64. if e.errno != errno.ENOENT:
  65. # Means the file was moved or deleted before the tempfile
  66. # could unlink it. Still sets self.file.close_called and
  67. # calls self.file.file.close() before the exception
  68. raise
  69. class InMemoryUploadedFile(UploadedFile):
  70. """
  71. A file uploaded into memory (i.e. stream-to-memory).
  72. """
  73. def __init__(self, file, field_name, name, content_type, size, charset, content_type_extra=None):
  74. super(InMemoryUploadedFile, self).__init__(file, name, content_type, size, charset, content_type_extra)
  75. self.field_name = field_name
  76. def open(self, mode=None):
  77. self.file.seek(0)
  78. def chunks(self, chunk_size=None):
  79. self.file.seek(0)
  80. yield self.read()
  81. def multiple_chunks(self, chunk_size=None):
  82. # Since it's in memory, we'll never have multiple chunks.
  83. return False
  84. class SimpleUploadedFile(InMemoryUploadedFile):
  85. """
  86. A simple representation of a file, which just has content, size, and a name.
  87. """
  88. def __init__(self, name, content, content_type='text/plain'):
  89. content = content or b''
  90. super(SimpleUploadedFile, self).__init__(BytesIO(content), None, name,
  91. content_type, len(content), None, None)
  92. @classmethod
  93. def from_dict(cls, file_dict):
  94. """
  95. Creates a SimpleUploadedFile object from
  96. a dictionary object with the following keys:
  97. - filename
  98. - content-type
  99. - content
  100. """
  101. return cls(file_dict['filename'],
  102. file_dict['content'],
  103. file_dict.get('content-type', 'text/plain'))