filestorage.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. # GNU MediaGoblin -- federated, autonomous media hosting
  2. # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
  3. #
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU Affero General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU Affero General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU Affero General Public License
  15. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. import io
  17. import os
  18. import shutil
  19. import six.moves.urllib.parse as urlparse
  20. from mediagoblin.storage import (
  21. StorageInterface,
  22. clean_listy_filepath,
  23. NoWebServing)
  24. class FileObjectAwareFile(io.FileIO):
  25. def write(self, data):
  26. if hasattr(data, 'read'):
  27. # We can call data.read(). It means that the data is a file-like
  28. # object, which should be saved RAM-friendly way
  29. shutil.copyfileobj(data, self)
  30. else:
  31. super(FileObjectAwareFile, self).write(data)
  32. class BasicFileStorage(StorageInterface):
  33. """
  34. Basic local filesystem implementation of storage API
  35. """
  36. local_storage = True
  37. def __init__(self, base_dir, base_url=None, **kwargs):
  38. """
  39. Keyword arguments:
  40. - base_dir: Base directory things will be served out of. MUST
  41. be an absolute path.
  42. - base_url: URL files will be served from
  43. """
  44. self.base_dir = base_dir
  45. self.base_url = base_url
  46. def _resolve_filepath(self, filepath):
  47. """
  48. Transform the given filepath into a local filesystem filepath.
  49. """
  50. return os.path.join(
  51. self.base_dir, *clean_listy_filepath(filepath))
  52. def file_exists(self, filepath):
  53. return os.path.exists(self._resolve_filepath(filepath))
  54. def get_file(self, filepath, mode='r'):
  55. # Make directories if necessary
  56. if len(filepath) > 1:
  57. directory = self._resolve_filepath(filepath[:-1])
  58. if not os.path.exists(directory):
  59. os.makedirs(directory)
  60. # Grab and return the file in the mode specified
  61. return FileObjectAwareFile(self._resolve_filepath(filepath), mode)
  62. def delete_file(self, filepath):
  63. """Delete file at filepath
  64. Raises OSError in case filepath is a directory."""
  65. #TODO: log error
  66. os.remove(self._resolve_filepath(filepath))
  67. def delete_dir(self, dirpath, recursive=False):
  68. """returns True on succes, False on failure"""
  69. dirpath = self._resolve_filepath(dirpath)
  70. # Shortcut the default and simple case of nonempty=F, recursive=F
  71. if recursive:
  72. try:
  73. shutil.rmtree(dirpath)
  74. except OSError as e:
  75. #TODO: log something here
  76. return False
  77. else: # recursively delete everything
  78. try:
  79. os.rmdir(dirpath)
  80. except OSError as e:
  81. #TODO: log something here
  82. return False
  83. return True
  84. def file_url(self, filepath):
  85. if not self.base_url:
  86. raise NoWebServing(
  87. "base_url not set, cannot provide file urls")
  88. return urlparse.urljoin(
  89. self.base_url,
  90. '/'.join(clean_listy_filepath(filepath)))
  91. def get_local_path(self, filepath):
  92. return self._resolve_filepath(filepath)
  93. def copy_local_to_storage(self, filename, filepath):
  94. """
  95. Copy this file from locally to the storage system.
  96. """
  97. # Make directories if necessary
  98. if len(filepath) > 1:
  99. directory = self._resolve_filepath(filepath[:-1])
  100. if not os.path.exists(directory):
  101. os.makedirs(directory)
  102. # This uses chunked copying of 16kb buffers (Py2.7):
  103. shutil.copy(filename, self.get_local_path(filepath))
  104. def get_file_size(self, filepath):
  105. return os.stat(self._resolve_filepath(filepath)).st_size