You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

uploadedfile.py 3.8KB

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