from django.core.files.storage import FileSystemStorage from django.core.files.storage import Storage from django.conf import settings from django.core.files import locks, File import hashlib import os import errno # A handy base-conversion thingie from base_n_utils import base_n_encode class OverwritingStorage(FileSystemStorage): """ FileSystemStorage that allows overwriting files """ def get_available_name(self, name): return name def _save(self, name, content): """ lifted from django/core/files/storage.py """ full_path = self.path(name) directory = os.path.dirname(full_path) if not os.path.exists(directory): os.makedirs(directory) elif not os.path.isdir(directory): raise IOError("%s exists and is not a directory." % directory) # This file has a file path that we can move. if hasattr(content, 'temporary_file_path'): file_move_safe(content.temporary_file_path(), full_path) content.close() # This is a normal uploadedfile that we can stream. else: # This fun binary flag incantation makes os.open throw an # OSError if the file already exists before we open it. try: fd = os.open(full_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, 'O_BINARY', 0)) locks.lock(fd, locks.LOCK_EX) for chunk in content.chunks(): os.write(fd, chunk) locks.unlock(fd) os.close(fd) except OSError, e: if e.errno != errno.EEXIST: raise else: # TODO: Should check if files are identical and shout if they are not pass if settings.FILE_UPLOAD_PERMISSIONS is not None: os.chmod(full_path, settings.FILE_UPLOAD_PERMISSIONS) return name def delete(self, name): """ Never deletes anything TODO: Could conceivably check if any ImageFields in the application are pointing to it """ pass @classmethod def hashed_path(cls, image): m = hashlib.md5() m.update(image.read()) num = int(m.hexdigest(), 16) md5_36 = base_n_encode(num, '0123456789abcdefghijklmnopqrstuvwxyz') pathname = settings.MEDIA_ROOT + '/images/' + md5_36[:2] + '/' + md5_36[2:4] + '/' + md5_36[4:6] + '/' + md5_36 + '.jpg' return pathname