aboutsummaryrefslogtreecommitdiffstats
path: root/mediagoblin/storage.py
diff options
context:
space:
mode:
Diffstat (limited to 'mediagoblin/storage.py')
-rw-r--r--mediagoblin/storage.py84
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):
"""