from django import forms import datetime import base64 import hmac, sha, simplejson """ http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1434 """ class S3UploadForm(forms.Form): key = forms.CharField(widget = forms.HiddenInput) AWSAccessKeyId = forms.CharField(widget = forms.HiddenInput) acl = forms.CharField(widget = forms.HiddenInput) success_action_redirect = forms.CharField(widget = forms.HiddenInput) policy = forms.CharField(widget = forms.HiddenInput) signature = forms.CharField(widget = forms.HiddenInput) Content_Type = forms.CharField(widget = forms.HiddenInput) file = forms.FileField() def __init__(self, aws_access_key, aws_secret_key, bucket, key, expires_after = datetime.timedelta(days = 30), acl = 'public-read', success_action_redirect = None, content_type = '', min_size = 0, max_size = None ): self.aws_access_key = aws_access_key self.aws_secret_key = aws_secret_key self.bucket = bucket self.key = key self.expires_after = expires_after self.acl = acl self.success_action_redirect = success_action_redirect self.content_type = content_type self.min_size = min_size self.max_size = max_size policy = base64.b64encode(self.calculate_policy()) signature = self.sign_policy(policy) initial = { 'key': self.key, 'AWSAccessKeyId': self.aws_access_key, 'acl': self.acl, 'policy': policy, 'signature': signature, 'Content-Type': self.content_type, } if self.success_action_redirect: initial['success_action_redirect'] = self.success_action_redirect super(S3UploadForm, self).__init__(initial = initial) # We need to manually rename the Content_Type field to Content-Type self.fields['Content-Type'] = self.fields['Content_Type'] del self.fields['Content_Type'] # Don't show success_action_redirect if it's not being used if not self.success_action_redirect: del self.fields['success_action_redirect'] def as_html(self): """ Use this instead of as_table etc, because S3 requires the file field come AFTER the hidden fields, but Django's normal form display methods position the visible fields BEFORE the hidden fields. """ html = ''.join(map(unicode, self.hidden_fields())) html += unicode(self['file']) return html def as_form_html(self, prefix='', suffix=''): html = """
""".strip() % (self.action(), self.as_html()) return html def is_multipart(self): return True def action(self): return 'https://%s.s3.amazonaws.com/' % self.bucket def calculate_policy(self): conditions = [ {'bucket': self.bucket}, {'acl': self.acl}, ['starts-with', '$key', self.key.replace('${filename}', '')], ["starts-with", "$Content-Type", self.content_type], ] if self.success_action_redirect: conditions.append( {'success_action_redirect': self.success_action_redirect}, ) policy_document = { "expiration": ( datetime.datetime.now() + self.expires_after ).isoformat().split('.')[0] + 'Z', "conditions": conditions, } return simplejson.dumps(policy_document, indent=2) def sign_policy(self, policy): return base64.b64encode( hmac.new(self.aws_secret_key, policy, sha).digest() )