storage.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import os
  2. import itertools
  3. import urlparse
  4. from mongoengine import *
  5. from django.conf import settings
  6. from django.core.files.storage import Storage
  7. from django.core.exceptions import ImproperlyConfigured
  8. class FileDocument(Document):
  9. """A document used to store a single file in GridFS.
  10. """
  11. file = FileField()
  12. class GridFSStorage(Storage):
  13. """A custom storage backend to store files in GridFS
  14. """
  15. def __init__(self, base_url=None):
  16. if base_url is None:
  17. base_url = settings.MEDIA_URL
  18. self.base_url = base_url
  19. self.document = FileDocument
  20. self.field = 'file'
  21. def delete(self, name):
  22. """Deletes the specified file from the storage system.
  23. """
  24. if self.exists(name):
  25. doc = self.document.objects.first()
  26. field = getattr(doc, self.field)
  27. self._get_doc_with_name(name).delete() # Delete the FileField
  28. field.delete() # Delete the FileDocument
  29. def exists(self, name):
  30. """Returns True if a file referened by the given name already exists in the
  31. storage system, or False if the name is available for a new file.
  32. """
  33. doc = self._get_doc_with_name(name)
  34. if doc:
  35. field = getattr(doc, self.field)
  36. return bool(field.name)
  37. else:
  38. return False
  39. def listdir(self, path=None):
  40. """Lists the contents of the specified path, returning a 2-tuple of lists;
  41. the first item being directories, the second item being files.
  42. """
  43. def name(doc):
  44. return getattr(doc, self.field).name
  45. docs = self.document.objects
  46. return [], [name(d) for d in docs if name(d)]
  47. def size(self, name):
  48. """Returns the total size, in bytes, of the file specified by name.
  49. """
  50. doc = self._get_doc_with_name(name)
  51. if doc:
  52. return getattr(doc, self.field).length
  53. else:
  54. raise ValueError("No such file or directory: '%s'" % name)
  55. def url(self, name):
  56. """Returns an absolute URL where the file's contents can be accessed
  57. directly by a web browser.
  58. """
  59. if self.base_url is None:
  60. raise ValueError("This file is not accessible via a URL.")
  61. return urlparse.urljoin(self.base_url, name).replace('\\', '/')
  62. def _get_doc_with_name(self, name):
  63. """Find the documents in the store with the given name
  64. """
  65. docs = self.document.objects
  66. doc = [d for d in docs if hasattr(getattr(d, self.field), 'name') and getattr(d, self.field).name == name]
  67. if doc:
  68. return doc[0]
  69. else:
  70. return None
  71. def _open(self, name, mode='rb'):
  72. doc = self._get_doc_with_name(name)
  73. if doc:
  74. return getattr(doc, self.field)
  75. else:
  76. raise ValueError("No file found with the name '%s'." % name)
  77. def get_available_name(self, name):
  78. """Returns a filename that's free on the target storage system, and
  79. available for new content to be written to.
  80. """
  81. file_root, file_ext = os.path.splitext(name)
  82. # If the filename already exists, add an underscore and a number (before
  83. # the file extension, if one exists) to the filename until the generated
  84. # filename doesn't exist.
  85. count = itertools.count(1)
  86. while self.exists(name):
  87. # file_ext includes the dot.
  88. name = os.path.join("%s_%s%s" % (file_root, count.next(), file_ext))
  89. return name
  90. def _save(self, name, content):
  91. doc = self.document()
  92. getattr(doc, self.field).put(content, filename=name)
  93. doc.save()
  94. return name