Skip to content

Instantly share code, notes, and snippets.

@FZambia
Created April 28, 2014 09:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FZambia/11366204 to your computer and use it in GitHub Desktop.
Save FZambia/11366204 to your computer and use it in GitHub Desktop.
webdav storage backend for django
# coding: utf-8
from urlparse import urljoin
from django.conf import settings
from django.core.files.storage import Storage
import logging
from urlparse import urlparse
import requests
logger = logging.getLogger('webdav')
class WebDAVException(Exception):
pass
class WebDAVStorage(Storage):
"""
WebDAV Storage class for Django
"""
def __init__(self, locations=settings.WEBDAV_LOCATIONS):
self._locations = locations
self._base_path = urlparse(settings.MEDIA_URL).path
def _get_full_path(self, location, name):
return urljoin(urljoin(location, self._base_path), name)
def exists(self, name):
logger.debug(u'checking existing of {0}'.format(name))
file_found = False
# check all dav instances for file existence,
# if file exist in one of them then we think that file exists
for location in self._locations:
logger.debug(u'checking via location {0}'.format(location))
response = requests.head(self._get_full_path(location, name))
if response.status_code == 200:
file_found = True
logger.debug(u'file found')
break
if not file_found:
logger.debug(u'file does not exist')
return file_found
def _save(self, name, content):
logger.debug(u'saving {0}'.format(name))
is_tmp_file = hasattr(content.file, 'temporary_file_path')
if is_tmp_file:
data = None
logger.debug(u"uploading from temporary file")
else:
# read content into memory
logger.debug(u"uploading from memory")
data = content.read()
for location in self._locations:
logger.debug(u'saving in {0}'.format(location))
if is_tmp_file:
# send file without loading it in memory
with open(content.file.temporary_file_path()) as f:
response = requests.put(self._get_full_path(location, name), data=f)
else:
response = requests.put(
self._get_full_path(location, name), data=data
)
if response.status_code not in (201, 204):
msg = u"uploading {0}: status code {1}".format(
self._get_full_path(location, name), response.status_code
)
logger.error(msg)
raise WebDAVException(msg)
logger.debug(u'{0} successfully saved'.format(name))
return name
def _open(self, name, mode):
raise NotImplementedError
def _read(self, name):
raise NotImplementedError()
def delete(self, name):
logger.debug(u'deleting {0}'.format(name))
for location in self._locations:
logger.debug(u'deleting in {0}'.format(location))
response = requests.delete(self._get_full_path(location, name))
if response.status_code not in (204,):
msg = u"deleting {0}: status code {1}".format(self._get_full_path(location, name), response.status_code)
logger.error(msg)
raise WebDAVException(msg)
logger.debug(u'{0} deleted'.format(name))
def url(self, name):
return urljoin(settings.MEDIA_URL, name)
def size(self, name):
logger.debug(u'getting {0} size'.format(name))
for location in self._locations:
logger.debug(u'getting via {0}'.format(location))
try:
response = requests.head(self._get_full_path(location, name), timeout=1)
except Exception as err:
logger.exception(err)
else:
return response.headers.get('Content-Length')
logger.error(u'file size not found')
return None
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment