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