david / django-storages

Support for many storages (S3, MogileFS, etc) in Django.

Clone this repository (size: 192.4 KB): HTTPS / SSH
$ hg clone http://code.welldev.org/django-storages
django-storages / S3Storage.py
    #   Introduced
1
1cbeb2a32019
from mimetypes import guess_type
2
1cbeb2a32019
import os
3
1cbeb2a32019
4
1cbeb2a32019
from django.core.exceptions import ImproperlyConfigured
5
1cbeb2a32019
from django.core.filestorage.base import Storage, RemoteFile
6
1cbeb2a32019
from django.core.filestorage.filesystem import FileSystemStorage
7
1cbeb2a32019
from django.utils.functional import curry
8
1cbeb2a32019
from django.conf import settings
9
1cbeb2a32019
10
1cbeb2a32019
ACCESS_KEY_NAME = 'AWS_ACCESS_KEY_ID'
11
1cbeb2a32019
SECRET_KEY_NAME = 'AWS_SECRET_ACCESS_KEY'
12
1cbeb2a32019
AWS_HEADERS = 'AWS_HEADERS'
13
1cbeb2a32019
14
1cbeb2a32019
try:
15
1cbeb2a32019
    from S3 import AWSAuthConnection, QueryStringAuthGenerator
16
1cbeb2a32019
except ImportError:
17
1cbeb2a32019
    raise ImproperlyConfigured, "Could not load amazon's S3 bindings.\
18
1cbeb2a32019
    \nSee http://developer.amazonwebservices.com/connect/entry.jspa?externalID=134"
19
1cbeb2a32019
20
1cbeb2a32019
class S3Storage(Storage):
21
1cbeb2a32019
    """Amazon Simple Storage Service"""
22
1cbeb2a32019
23
1cbeb2a32019
    def __init__(self, bucket=settings.AWS_STORAGE_BUCKET_NAME, 
24
1cbeb2a32019
            access_key=None, secret_key=None, acl='public-read', 
25
1cbeb2a32019
            calling_format=settings.AWS_CALLING_FORMAT):
26
1cbeb2a32019
        self.bucket = bucket
27
1cbeb2a32019
        self.acl = acl
28
1cbeb2a32019
29
1cbeb2a32019
        if not access_key and not secret_key:
30
1cbeb2a32019
             access_key, secret_key = self._get_access_keys()
31
1cbeb2a32019
32
1cbeb2a32019
        self.connection = AWSAuthConnection(access_key, secret_key, 
33
1cbeb2a32019
                            calling_format=calling_format)
34
1cbeb2a32019
        self.generator = QueryStringAuthGenerator(access_key, secret_key, 
35
1cbeb2a32019
                            calling_format=calling_format, is_secure=False)
36
1cbeb2a32019
        
37
1cbeb2a32019
        self.headers = getattr(settings, AWS_HEADERS, {})
38
1cbeb2a32019
39
1cbeb2a32019
    def _get_access_keys(self):
40
1cbeb2a32019
        access_key = getattr(settings, ACCESS_KEY_NAME, None)
41
1cbeb2a32019
        secret_key = getattr(settings, SECRET_KEY_NAME, None)
42
1cbeb2a32019
        if (access_key or secret_key) and (not access_key or not secret_key):
43
1cbeb2a32019
            access_key = os.environ.get(ACCESS_KEY_NAME)
44
1cbeb2a32019
            secret_key = os.environ.get(SECRET_KEY_NAME)
45
1cbeb2a32019
46
1cbeb2a32019
        if access_key and secret_key:
47
1cbeb2a32019
            # Both were provided, so use them
48
1cbeb2a32019
            return access_key, secret_key
49
1cbeb2a32019
50
1cbeb2a32019
        return None, None
51
1cbeb2a32019
52
1cbeb2a32019
    def _get_connection(self):
53
1cbeb2a32019
        return AWSAuthConnection(*self._get_access_keys())
54
1cbeb2a32019
55
1cbeb2a32019
    def _put_file(self, filename, raw_contents):
56
1cbeb2a32019
        content_type = guess_type(filename)[0] or "application/x-octet-stream"
57
1cbeb2a32019
        self.headers.update({'x-amz-acl':  self.acl, 'Content-Type': content_type})
58
1cbeb2a32019
        response = self.connection.put(self.bucket, filename, raw_contents, self.headers)
59
1cbeb2a32019
60
1cbeb2a32019
    def url(self, filename):
61
1cbeb2a32019
        return self.generator.make_bare_url(self.bucket, filename)
62
1cbeb2a32019
    
63
1cbeb2a32019
    path = url
64
1cbeb2a32019
65
1cbeb2a32019
    def filesize(self, filename):
66
1cbeb2a32019
        response = self.connection.make_request('HEAD', self.bucket, filename)
67
1cbeb2a32019
        return int(response.getheader('Content-Length'))
68
1cbeb2a32019
69
1cbeb2a32019
    def open(self, filename, mode='rb'):
70
1cbeb2a32019
        response = self.connection.get(self.bucket, filename)
71
1cbeb2a32019
        writer = curry(self._put_file, filename)
72
1cbeb2a32019
        return RemoteFile(self, response.object.data, mode, writer)
73
1cbeb2a32019
74
1cbeb2a32019
    def exists(self, filename):
75
1cbeb2a32019
        response = self.connection.make_request('HEAD', self.bucket, filename)
76
1cbeb2a32019
        return response.status == 200
77
1cbeb2a32019
78
1cbeb2a32019
    def save(self, filename, raw_contents):
79
1cbeb2a32019
        filename = self.get_available_filename(filename)
80
1cbeb2a32019
        self._put_file(filename, raw_contents)
81
1cbeb2a32019
        return filename
82
1cbeb2a32019
    
83
1cbeb2a32019
    ## UNCOMMENT BELOW IF NECESSARY
84
1cbeb2a32019
    
85
1cbeb2a32019
    #def delete(self, filename):
86
1cbeb2a32019
    #    """ Do not delete default images. """
87
1cbeb2a32019
    #    if not filename.endswith('default.jpg') and not filename.endswith('guest.jpg'):
88
1cbeb2a32019
    #        self.connection.delete(self.bucket, filename)
89
1cbeb2a32019
90
1cbeb2a32019
    #def get_available_filename(self, filename):
91
1cbeb2a32019
    #    """ Overwrite existing file with the same name. """
92
1cbeb2a32019
    #    return filename