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.
169 lines
4.5 KiB
169 lines
4.5 KiB
import os |
|
|
|
from django.utils.encoding import smart_str, smart_unicode |
|
|
|
try: |
|
from cStringIO import StringIO |
|
except ImportError: |
|
from StringIO import StringIO |
|
|
|
class File(object): |
|
DEFAULT_CHUNK_SIZE = 64 * 2**10 |
|
|
|
def __init__(self, file): |
|
self.file = file |
|
self._name = file.name |
|
self._mode = file.mode |
|
self._closed = False |
|
|
|
def __str__(self): |
|
return smart_str(self.name or '') |
|
|
|
def __unicode__(self): |
|
return smart_unicode(self.name or u'') |
|
|
|
def __repr__(self): |
|
return "<%s: %s>" % (self.__class__.__name__, self or "None") |
|
|
|
def __nonzero__(self): |
|
return not not self.name |
|
|
|
def __len__(self): |
|
return self.size |
|
|
|
def _get_name(self): |
|
return self._name |
|
name = property(_get_name) |
|
|
|
def _get_mode(self): |
|
return self._mode |
|
mode = property(_get_mode) |
|
|
|
def _get_closed(self): |
|
return self._closed |
|
closed = property(_get_closed) |
|
|
|
def _get_size(self): |
|
if not hasattr(self, '_size'): |
|
if hasattr(self.file, 'size'): |
|
self._size = self.file.size |
|
elif os.path.exists(self.file.name): |
|
self._size = os.path.getsize(self.file.name) |
|
else: |
|
raise AttributeError("Unable to determine the file's size.") |
|
return self._size |
|
|
|
def _set_size(self, size): |
|
self._size = size |
|
|
|
size = property(_get_size, _set_size) |
|
|
|
def chunks(self, chunk_size=None): |
|
""" |
|
Read the file and yield chucks of ``chunk_size`` bytes (defaults to |
|
``UploadedFile.DEFAULT_CHUNK_SIZE``). |
|
""" |
|
if not chunk_size: |
|
chunk_size = self.__class__.DEFAULT_CHUNK_SIZE |
|
|
|
if hasattr(self, 'seek'): |
|
self.seek(0) |
|
# Assume the pointer is at zero... |
|
counter = self.size |
|
|
|
while counter > 0: |
|
yield self.read(chunk_size) |
|
counter -= chunk_size |
|
|
|
def multiple_chunks(self, chunk_size=None): |
|
""" |
|
Returns ``True`` if you can expect multiple chunks. |
|
|
|
NB: If a particular file representation is in memory, subclasses should |
|
always return ``False`` -- there's no good reason to read from memory in |
|
chunks. |
|
""" |
|
if not chunk_size: |
|
chunk_size = self.DEFAULT_CHUNK_SIZE |
|
return self.size > chunk_size |
|
|
|
def xreadlines(self): |
|
return iter(self) |
|
|
|
def readlines(self): |
|
return list(self.xreadlines()) |
|
|
|
def __iter__(self): |
|
# Iterate over this file-like object by newlines |
|
buffer_ = None |
|
for chunk in self.chunks(): |
|
chunk_buffer = StringIO(chunk) |
|
|
|
for line in chunk_buffer: |
|
if buffer_: |
|
line = buffer_ + line |
|
buffer_ = None |
|
|
|
# If this is the end of a line, yield |
|
# otherwise, wait for the next round |
|
if line[-1] in ('\n', '\r'): |
|
yield line |
|
else: |
|
buffer_ = line |
|
|
|
if buffer_ is not None: |
|
yield buffer_ |
|
|
|
def open(self, mode=None): |
|
if not self.closed: |
|
self.seek(0) |
|
elif os.path.exists(self.file.name): |
|
self.file = open(self.file.name, mode or self.file.mode) |
|
else: |
|
raise ValueError("The file cannot be reopened.") |
|
|
|
def seek(self, position): |
|
self.file.seek(position) |
|
|
|
def tell(self): |
|
return self.file.tell() |
|
|
|
def read(self, num_bytes=None): |
|
if num_bytes is None: |
|
return self.file.read() |
|
return self.file.read(num_bytes) |
|
|
|
def write(self, content): |
|
if not self.mode.startswith('w'): |
|
raise IOError("File was not opened with write access.") |
|
self.file.write(content) |
|
|
|
def flush(self): |
|
if not self.mode.startswith('w'): |
|
raise IOError("File was not opened with write access.") |
|
self.file.flush() |
|
|
|
def close(self): |
|
self.file.close() |
|
self._closed = True |
|
|
|
class ContentFile(File): |
|
""" |
|
A File-like object that takes just raw content, rather than an actual file. |
|
""" |
|
def __init__(self, content): |
|
self.file = StringIO(content or '') |
|
self.size = len(content or '') |
|
self.file.seek(0) |
|
self._closed = False |
|
|
|
def __str__(self): |
|
return 'Raw content' |
|
|
|
def __nonzero__(self): |
|
return True |
|
|
|
def open(self, mode=None): |
|
if self._closed: |
|
self._closed = False |
|
self.seek(0)
|
|
|