diff options
Diffstat (limited to 'mediagoblin/storage.py')
-rw-r--r-- | mediagoblin/storage.py | 84 |
1 files changed, 79 insertions, 5 deletions
diff --git a/mediagoblin/storage.py b/mediagoblin/storage.py index d484be1f..f9563031 100644 --- a/mediagoblin/storage.py +++ b/mediagoblin/storage.py @@ -1,5 +1,5 @@ # GNU MediaGoblin -- federated, autonomous media hosting -# Copyright (C) 2011 Free Software Foundation, Inc +# Copyright (C) 2011 MediaGoblin contributors. See AUTHORS. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by @@ -19,6 +19,8 @@ import shutil import urlparse import uuid import cloudfiles +import mimetypes +import tempfile from werkzeug.utils import secure_filename @@ -226,6 +228,10 @@ class BasicFileStorage(StorageInterface): return self._resolve_filepath(filepath) +# ---------------------------------------------------- +# OpenStack/Rackspace Cloud's Swift/CloudFiles support +# ---------------------------------------------------- + class CloudFilesStorage(StorageInterface): def __init__(self, **kwargs): self.param_container = kwargs.get('cloudfiles_container') @@ -254,6 +260,8 @@ class CloudFilesStorage(StorageInterface): self.container = self.connection.get_container( self.param_container) + self.container_uri = self.container.public_uri() + def _resolve_filepath(self, filepath): return '/'.join( clean_listy_filepath(filepath)) @@ -266,7 +274,10 @@ class CloudFilesStorage(StorageInterface): except cloudfiles.errors.NoSuchObject: return False - def get_file(self, filepath, mode='r'): + def get_file(self, filepath, *args, **kwargs): + """ + - Doesn't care about the "mode" argument + """ try: obj = self.container.get_object( self._resolve_filepath(filepath)) @@ -274,16 +285,79 @@ class CloudFilesStorage(StorageInterface): obj = self.container.create_object( self._resolve_filepath(filepath)) - return obj + mimetype = mimetypes.guess_type( + filepath[-1]) + + if mimetype: + obj.content_type = mimetype[0] + + return CloudFilesStorageObjectWrapper(obj, *args, **kwargs) def delete_file(self, filepath): # TODO: Also delete unused directories if empty (safely, with # checks to avoid race conditions). - self.container.delete_object(filepath) + self.container.delete_object( + self._resolve_filepath(filepath)) def file_url(self, filepath): - return self.get_file(filepath).public_uri() + return '/'.join([ + self.container_uri, + self._resolve_filepath(filepath)]) + + +class CloudFilesStorageObjectWrapper(): + """ + Wrapper for python-cloudfiles's cloudfiles.storage_object.Object + used to circumvent the mystic `medium.jpg` corruption issue, where + we had both python-cloudfiles and PIL doing buffering on both + ends and that breaking things. + + This wrapper currently meets mediagoblin's needs for a public_store + file-like object. + """ + def __init__(self, storage_object, *args, **kwargs): + self.storage_object = storage_object + + def read(self, *args, **kwargs): + return self.storage_object.read(*args, **kwargs) + + def write(self, data, *args, **kwargs): + """ + write data to the cloudfiles storage object + + The original motivation for this wrapper is to ensure + that buffered writing to a cloudfiles storage object does not overwrite + any preexisting data. + + Currently this method does not support any write modes except "append". + However if we should need it it would be easy implement. + """ + if self.storage_object.size and type(data) == str: + data = self.read() + data + + self.storage_object.write(data, *args, **kwargs) + + def close(self): + pass + + def __enter__(self): + """ + Context Manager API implementation + http://docs.python.org/library/stdtypes.html#context-manager-types + """ + return self + + def __exit__(self, *exc_info): + """ + Context Manger API implementation + see self.__enter__() + """ + self.close() + +# ------------ +# MountStorage +# ------------ class MountStorage(StorageInterface): """ |