Ticket #6390: S3_6390.20080225.py

File S3_6390.20080225.py, 3.2 KB (added by David Larlet, 15 years ago)

Functional S3 backend, use only Amazon's S3 lib (warning it overwrites filename)

Line 
1from mimetypes import guess_type
2from StringIO import StringIO
3import urlparse
4import os
5
6from django.core.exceptions import ImproperlyConfigured
7from django.core.filestorage.base import Backend, RemoteFile
8from django.core.filestorage.filesystem import FileSystemBackend
9from django.utils.functional import curry
10
11ACCESS_KEY_NAME = 'AWS_ACCESS_KEY_ID'
12SECRET_KEY_NAME = 'AWS_SECRET_ACCESS_KEY'
13
14try:
15    from _lib.amazon.S3 import AWSAuthConnection, QueryStringAuthGenerator, CallingFormat
16except ImportError:
17    raise ImproperlyConfigured, "Could not load Amazon's S3 bindings.\
18    \nSee http://developer.amazonwebservices.com/connect/entry.jspa?externalID=134"
19
20class S3Backend(Backend):
21    """Amazon Simple Storage Service"""
22
23    def __init__(self, bucket, access_key=None, secret_key=None, 
24            acl='public-read', calling_format=CallingFormat.REGULAR):
25        self.bucket = bucket
26        self.acl = acl
27
28        if not access_key and not secret_key:
29             access_key, secret_key = self._get_access_keys()
30
31        self.connection = AWSAuthConnection(access_key, secret_key, 
32                            calling_format=calling_format)
33        self.generator = QueryStringAuthGenerator(access_key, secret_key, 
34                            calling_format=calling_format, is_secure=False)
35
36    def _get_access_keys(self):
37        access_key = getattr(settings, ACCESS_KEY_NAME, None)
38        secret_key = getattr(settings, SECRET_KEY_NAME, None)
39        if (access_key or secret_key) and (not access_key or not secret_key):
40            access_key = os.environ.get(ACCESS_KEY_NAME)
41            secret_key = os.environ.get(SECRET_KEY_NAME)
42
43        if access_key and secret_key:
44            # Both were provided, so use them
45            return access_key, secret_key
46
47        return None, None
48
49    def _get_connection(self):
50        return AWSAuthConnection(*self._get_access_keys())
51
52    def _put_file(self, filename, raw_contents):
53        content_type = guess_type(filename)[0] or "application/x-octet-stream"
54        headers = {'x-amz-acl':  self.acl, 'Content-Type': content_type}
55        response = self.connection.put(self.bucket, filename, raw_contents, headers)
56
57    def get_absolute_url(self, filename):
58        return self.generator.make_bare_url(self.bucket, filename)
59
60    def get_filesize(self, filename):
61        response = self.connection.make_request('HEAD', self.bucket, filename)
62        return int(response.getheader('Content-Length'))
63
64    def open_file(self, filename, mode='rb'):
65        response = self.connection.get(self.bucket, filename)
66        writer = curry(self._put_file, filename)
67        return RemoteFile(self, response.object.data, mode, writer)
68
69    def file_exists(self, filename):
70        response = self.connection.make_request('HEAD', self.bucket, filename)
71        return response.status == 200
72
73    def save_file(self, filename, raw_contents):
74        filename = self.get_available_filename(filename)
75        self._put_file(filename, raw_contents)
76        return filename
77
78    def delete_file(self, filename):
79        self.connection.delete(self.bucket, filename)
80
81    def get_available_filename(self, filename):
82        """ Overwrite existing file with the same name. """
83        return filename
Back to Top