Django

Code

Ticket #6390: S3_6390.20080321.py

File S3_6390.20080321.py, 3.2 kB (added by david, 5 months ago)

New S3 storage which works with the latest storage patch (14) with new functions' names, I'm not totally satisfied with the way arguments are passed, I'll try to do it better

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