Created
November 29, 2012 07:47
-
-
Save derekkwok/4167446 to your computer and use it in GitHub Desktop.
A subclass of FileSystemStorage that checks if content.file has attribute 'temporary_file_path'
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import errno, os | |
from django.conf import settings | |
from django.core.files import locks | |
from django.core.files.move import file_move_safe | |
from django.core.files.storage import FileSystemStorage | |
class CustomStorage(FileSystemStorage): | |
""" | |
This storage class tries to move files instead of copy when saving via | |
FieldFile. | |
""" | |
def _save(self, name, content): | |
full_path = self.path(name) | |
# Create any intermediate directories that do not exist. | |
# Note that there is a race between os.path.exists and os.makedirs: | |
# if os.makedirs fails with EEXIST, the directory was created | |
# concurrently, and we can continue normally. Refs #16082. | |
directory = os.path.dirname(full_path) | |
if not os.path.exists(directory): | |
try: | |
os.makedirs(directory) | |
except OSError as e: | |
if e.errno != errno.EEXIST: | |
raise | |
if not os.path.isdir(directory): | |
raise IOError("%s exists and is not a directory." % directory) | |
# There's a potential race condition between get_available_name and | |
# saving the file; it's possible that two threads might return the | |
# same name, at which point all sorts of fun happens. So we need to | |
# try to create the file, but if it already exists we have to go back | |
# to get_available_name() and try again. | |
while True: | |
try: | |
# This file has a file path that we can move. | |
if hasattr(content.file, 'temporary_file_path'): | |
file_move_safe(content.file.temporary_file_path(), full_path) | |
content.close() | |
elif 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. | |
flags = (os.O_WRONLY | os.O_CREAT | os.O_EXCL | | |
getattr(os, 'O_BINARY', 0)) | |
# The current umask value is masked out by os.open! | |
fd = os.open(full_path, flags, 0o666) | |
try: | |
locks.lock(fd, locks.LOCK_EX) | |
_file = None | |
for chunk in content.chunks(): | |
if _file is None: | |
mode = 'wb' if isinstance(chunk, bytes) else 'wt' | |
_file = os.fdopen(fd, mode) | |
_file.write(chunk) | |
finally: | |
locks.unlock(fd) | |
if _file is not None: | |
_file.close() | |
else: | |
os.close(fd) | |
except OSError as e: | |
if e.errno == errno.EEXIST: | |
# Ooops, the file exists. We need a new file name. | |
name = self.get_available_name(name) | |
full_path = self.path(name) | |
else: | |
raise | |
else: | |
# OK, the file save worked. Break out of the loop. | |
break | |
if settings.FILE_UPLOAD_PERMISSIONS is not None: | |
os.chmod(full_path, settings.FILE_UPLOAD_PERMISSIONS) | |
return name |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment