170 lines
4.5 KiB
Python
170 lines
4.5 KiB
Python
|
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)
|