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
| commit 1: | d8a8ae7b1381 |
| parent 0: | 1cbeb2a32019 |
| branch: | default |
Add a README file with installation instructions
2 years ago
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 |
